summaryrefslogtreecommitdiffstats
path: root/Lib/setuptools
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/setuptools')
-rw-r--r--Lib/setuptools/__init__.py64
-rwxr-xr-xLib/setuptools/archive_util.py200
-rwxr-xr-xLib/setuptools/cli.exebin0 -> 6144 bytes
-rw-r--r--Lib/setuptools/command/__init__.py19
-rwxr-xr-xLib/setuptools/command/alias.py79
-rw-r--r--Lib/setuptools/command/bdist_egg.py449
-rwxr-xr-xLib/setuptools/command/bdist_rpm.py37
-rw-r--r--Lib/setuptools/command/build_ext.py285
-rw-r--r--Lib/setuptools/command/build_py.py192
-rwxr-xr-xLib/setuptools/command/develop.py116
-rwxr-xr-xLib/setuptools/command/easy_install.py1555
-rwxr-xr-xLib/setuptools/command/egg_info.py365
-rw-r--r--Lib/setuptools/command/install.py101
-rwxr-xr-xLib/setuptools/command/install_egg_info.py81
-rw-r--r--Lib/setuptools/command/install_lib.py76
-rwxr-xr-xLib/setuptools/command/install_scripts.py56
-rwxr-xr-xLib/setuptools/command/rotate.py57
-rwxr-xr-xLib/setuptools/command/saveopts.py24
-rwxr-xr-xLib/setuptools/command/sdist.py163
-rwxr-xr-xLib/setuptools/command/setopt.py158
-rw-r--r--Lib/setuptools/command/test.py119
-rwxr-xr-xLib/setuptools/command/upload.py178
-rw-r--r--Lib/setuptools/depends.py239
-rw-r--r--Lib/setuptools/dist.py798
-rw-r--r--Lib/setuptools/extension.py35
-rwxr-xr-xLib/setuptools/gui.exebin0 -> 6144 bytes
-rwxr-xr-xLib/setuptools/package_index.py674
-rwxr-xr-xLib/setuptools/sandbox.py203
-rwxr-xr-xLib/setuptools/site-patch.py74
-rw-r--r--Lib/setuptools/tests/__init__.py364
-rwxr-xr-xLib/setuptools/tests/api_tests.txt330
-rw-r--r--Lib/setuptools/tests/test_resources.py483
32 files changed, 7574 insertions, 0 deletions
diff --git a/Lib/setuptools/__init__.py b/Lib/setuptools/__init__.py
new file mode 100644
index 0000000..3921ce2
--- /dev/null
+++ b/Lib/setuptools/__init__.py
@@ -0,0 +1,64 @@
+"""Extensions to the 'distutils' for large or complex distributions"""
+from setuptools.extension import Extension, Library
+from setuptools.dist import Distribution, Feature, _get_unpatched
+import distutils.core, setuptools.command
+from setuptools.depends import Require
+from distutils.core import Command as _Command
+from distutils.util import convert_path
+import os.path
+
+__version__ = '0.7a1'
+__all__ = [
+ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require',
+ 'find_packages'
+]
+
+bootstrap_install_from = None
+
+def find_packages(where='.', exclude=()):
+ """Return a list all Python packages found within directory 'where'
+
+ 'where' should be supplied as a "cross-platform" (i.e. URL-style) path; it
+ will be converted to the appropriate local path syntax. 'exclude' is a
+ sequence of package names to exclude; '*' can be used as a wildcard in the
+ names, such that 'foo.*' will exclude all subpackages of 'foo' (but not
+ 'foo' itself).
+ """
+ out = []
+ stack=[(convert_path(where), '')]
+ while stack:
+ where,prefix = stack.pop(0)
+ for name in os.listdir(where):
+ fn = os.path.join(where,name)
+ if (os.path.isdir(fn) and
+ os.path.isfile(os.path.join(fn,'__init__.py'))
+ ):
+ out.append(prefix+name); stack.append((fn,prefix+name+'.'))
+ for pat in exclude:
+ from fnmatch import fnmatchcase
+ out = [item for item in out if not fnmatchcase(item,pat)]
+ return out
+
+setup = distutils.core.setup
+
+_Command = _get_unpatched(_Command)
+
+class Command(_Command):
+ __doc__ = _Command.__doc__
+
+ command_consumes_arguments = False
+
+ def __init__(self, dist, **kw):
+ # Add support for keyword arguments
+ _Command.__init__(self,dist)
+ for k,v in kw.items():
+ setattr(self,k,v)
+
+ def reinitialize_command(self, command, reinit_subcommands=0, **kw):
+ cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
+ for k,v in kw.items():
+ setattr(cmd,k,v) # update command with keywords
+ return cmd
+
+import distutils.core
+distutils.core.Command = Command # we can't patch distutils.cmd, alas
diff --git a/Lib/setuptools/archive_util.py b/Lib/setuptools/archive_util.py
new file mode 100755
index 0000000..dd9c684
--- /dev/null
+++ b/Lib/setuptools/archive_util.py
@@ -0,0 +1,200 @@
+"""Utilities for extracting common archive formats"""
+
+
+__all__ = [
+ "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter",
+ "UnrecognizedFormat", "extraction_drivers", "unpack_directory",
+]
+
+import zipfile, tarfile, os, shutil
+from pkg_resources import ensure_directory
+from distutils.errors import DistutilsError
+
+class UnrecognizedFormat(DistutilsError):
+ """Couldn't recognize the archive type"""
+
+def default_filter(src,dst):
+ """The default progress/filter callback; returns True for all files"""
+ return dst
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def unpack_archive(filename, extract_dir, progress_filter=default_filter,
+ drivers=None
+):
+ """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
+
+ `progress_filter` is a function taking two arguments: a source path
+ internal to the archive ('/'-separated), and a filesystem path where it
+ will be extracted. The callback must return the desired extract path
+ (which may be the same as the one passed in), or else ``None`` to skip
+ that file or directory. The callback can thus be used to report on the
+ progress of the extraction, as well as to filter the items extracted or
+ alter their extraction paths.
+
+ `drivers`, if supplied, must be a non-empty sequence of functions with the
+ same signature as this function (minus the `drivers` argument), that raise
+ ``UnrecognizedFormat`` if they do not support extracting the designated
+ archive type. The `drivers` are tried in sequence until one is found that
+ does not raise an error, or until all are exhausted (in which case
+ ``UnrecognizedFormat`` is raised). If you do not supply a sequence of
+ drivers, the module's ``extraction_drivers`` constant will be used, which
+ means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that
+ order.
+ """
+ for driver in drivers or extraction_drivers:
+ try:
+ driver(filename, extract_dir, progress_filter)
+ except UnrecognizedFormat:
+ continue
+ else:
+ return
+ else:
+ raise UnrecognizedFormat(
+ "Not a recognized archive type: %s" % filename
+ )
+
+
+
+
+
+
+
+def unpack_directory(filename, extract_dir, progress_filter=default_filter):
+ """"Unpack" a directory, using the same interface as for archives
+
+ Raises ``UnrecognizedFormat`` if `filename` is not a directory
+ """
+ if not os.path.isdir(filename):
+ raise UnrecognizedFormat("%s is not a directory" % (filename,))
+
+ paths = {filename:('',extract_dir)}
+ for base, dirs, files in os.walk(filename):
+ src,dst = paths[base]
+ for d in dirs:
+ paths[os.path.join(base,d)] = src+d+'/', os.path.join(dst,d)
+ for f in files:
+ name = src+f
+ target = os.path.join(dst,f)
+ target = progress_filter(src+f, target)
+ if not target:
+ continue # skip non-files
+ ensure_directory(target)
+ f = os.path.join(base,f)
+ shutil.copyfile(f, target)
+ shutil.copystat(f, target)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
+ """Unpack zip `filename` to `extract_dir`
+
+ Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined
+ by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation
+ of the `progress_filter` argument.
+ """
+
+ if not zipfile.is_zipfile(filename):
+ raise UnrecognizedFormat("%s is not a zip file" % (filename,))
+
+ z = zipfile.ZipFile(filename)
+ try:
+ for info in z.infolist():
+ name = info.filename
+
+ # don't extract absolute paths or ones with .. in them
+ if name.startswith('/') or '..' in name:
+ continue
+
+ target = os.path.join(extract_dir, *name.split('/'))
+ target = progress_filter(name, target)
+ if not target:
+ continue
+ if name.endswith('/'):
+ # directory
+ ensure_directory(target)
+ else:
+ # file
+ ensure_directory(target)
+ data = z.read(info.filename)
+ f = open(target,'wb')
+ try:
+ f.write(data)
+ finally:
+ f.close()
+ del data
+ finally:
+ z.close()
+
+
+def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):
+ """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
+
+ Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined
+ by ``tarfile.open()``). See ``unpack_archive()`` for an explanation
+ of the `progress_filter` argument.
+ """
+
+ try:
+ tarobj = tarfile.open(filename)
+ except tarfile.TarError:
+ raise UnrecognizedFormat(
+ "%s is not a compressed or uncompressed tar file" % (filename,)
+ )
+
+ try:
+ tarobj.chown = lambda *args: None # don't do any chowning!
+ for member in tarobj:
+ if member.isfile() or member.isdir():
+ name = member.name
+ # don't extract absolute paths or ones with .. in them
+ if not name.startswith('/') and '..' not in name:
+ dst = os.path.join(extract_dir, *name.split('/'))
+ dst = progress_filter(name, dst)
+ if dst:
+ if dst.endswith(os.sep):
+ dst = dst[:-1]
+ tarobj._extract_member(member,dst) # XXX Ugh
+ return True
+ finally:
+ tarobj.close()
+
+
+
+
+extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile
diff --git a/Lib/setuptools/cli.exe b/Lib/setuptools/cli.exe
new file mode 100755
index 0000000..fc83339
--- /dev/null
+++ b/Lib/setuptools/cli.exe
Binary files differ
diff --git a/Lib/setuptools/command/__init__.py b/Lib/setuptools/command/__init__.py
new file mode 100644
index 0000000..bff53e7
--- /dev/null
+++ b/Lib/setuptools/command/__init__.py
@@ -0,0 +1,19 @@
+__all__ = [
+ 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
+ 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
+ 'sdist', 'setopt', 'test', 'upload', 'install_egg_info', 'install_scripts',
+]
+
+import sys
+if sys.version>='2.5':
+ # In Python 2.5 and above, distutils includes its own upload command
+ __all__.remove('upload')
+
+
+from distutils.command.bdist import bdist
+
+if 'egg' not in bdist.format_commands:
+ bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
+ bdist.format_commands.append('egg')
+
+del bdist, sys
diff --git a/Lib/setuptools/command/alias.py b/Lib/setuptools/command/alias.py
new file mode 100755
index 0000000..1df474a
--- /dev/null
+++ b/Lib/setuptools/command/alias.py
@@ -0,0 +1,79 @@
+import distutils, os
+from setuptools import Command
+from distutils.util import convert_path
+from distutils import log
+from distutils.errors import *
+from setuptools.command.setopt import edit_config, option_base, config_file
+
+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]:
+ return repr(arg)
+ return arg
+
+
+class alias(option_base):
+ """Define a shortcut that invokes one or more commands"""
+
+ description = "define a shortcut to invoke one or more commands"
+ command_consumes_arguments = True
+
+ user_options = [
+ ('remove', 'r', 'remove (unset) the alias'),
+ ] + option_base.user_options
+
+ boolean_options = option_base.boolean_options + ['remove']
+
+ def initialize_options(self):
+ option_base.initialize_options(self)
+ self.args = None
+ self.remove = None
+
+ def finalize_options(self):
+ option_base.finalize_options(self)
+ if self.remove and len(self.args)<>1:
+ raise DistutilsOptionError(
+ "Must specify exactly one argument (the alias name) when "
+ "using --remove"
+ )
+
+ def run(self):
+ aliases = self.distribution.get_option_dict('aliases')
+
+ if not self.args:
+ print "Command Aliases"
+ print "---------------"
+ for alias in aliases:
+ print "setup.py alias", format_alias(alias, aliases)
+ return
+
+ elif len(self.args)==1:
+ alias, = self.args
+ if self.remove:
+ command = None
+ elif alias in aliases:
+ print "setup.py alias", format_alias(alias, aliases)
+ return
+ else:
+ print "No alias definition found for %r" % alias
+ return
+ else:
+ alias = self.args[0]
+ command = ' '.join(map(shquote,self.args[1:]))
+
+ edit_config(self.filename, {'aliases': {alias:command}}, self.dry_run)
+
+
+def format_alias(name, aliases):
+ source, command = aliases[name]
+ if source == config_file('global'):
+ source = '--global-config '
+ elif source == config_file('user'):
+ source = '--user-config '
+ elif source == config_file('local'):
+ source = ''
+ else:
+ source = '--filename=%r' % source
+ return source+name+' '+command
diff --git a/Lib/setuptools/command/bdist_egg.py b/Lib/setuptools/command/bdist_egg.py
new file mode 100644
index 0000000..617d88d
--- /dev/null
+++ b/Lib/setuptools/command/bdist_egg.py
@@ -0,0 +1,449 @@
+"""setuptools.command.bdist_egg
+
+Build .egg distributions"""
+
+# This module should be kept compatible with Python 2.3
+import sys, os, marshal
+from setuptools import Command
+from distutils.dir_util import remove_tree, mkpath
+from distutils.sysconfig import get_python_version, get_python_lib
+from distutils import log
+from pkg_resources import get_build_platform, Distribution
+from types import CodeType
+from setuptools.extension import Library
+
+def write_stub(resource, pyfile):
+ f = open(pyfile,'w')
+ f.write('\n'.join([
+ "def __bootstrap__():",
+ " global __bootstrap__, __loader__, __file__",
+ " import sys, pkg_resources, imp",
+ " __file__ = pkg_resources.resource_filename(__name__,%r)"
+ % resource,
+ " del __bootstrap__, __loader__",
+ " imp.load_dynamic(__name__,__file__)",
+ "__bootstrap__()",
+ "" # terminal \n
+ ]))
+ f.close()
+
+# stub __init__.py for packages distributed without one
+NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)'
+
+
+
+
+
+
+
+
+
+
+class bdist_egg(Command):
+
+ description = "create an \"egg\" distribution"
+
+ user_options = [
+ ('bdist-dir=', 'b',
+ "temporary directory for creating the distribution"),
+ ('plat-name=', 'p',
+ "platform name to embed in generated filenames "
+ "(default: %s)" % get_build_platform()),
+ ('exclude-source-files', None,
+ "remove all .py files from the generated egg"),
+ ('keep-temp', 'k',
+ "keep the pseudo-installation tree around after " +
+ "creating the distribution archive"),
+ ('dist-dir=', 'd',
+ "directory to put final built distributions in"),
+ ('skip-build', None,
+ "skip rebuilding everything (for testing/debugging)"),
+ ]
+
+ boolean_options = [
+ 'keep-temp', 'skip-build', 'exclude-source-files'
+ ]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ def initialize_options (self):
+ self.bdist_dir = None
+ self.plat_name = None
+ self.keep_temp = 0
+ self.dist_dir = None
+ self.skip_build = 0
+ self.egg_output = None
+ self.exclude_source_files = None
+
+
+ def finalize_options(self):
+ ei_cmd = self.get_finalized_command("egg_info")
+ self.egg_info = ei_cmd.egg_info
+
+ if self.bdist_dir is None:
+ bdist_base = self.get_finalized_command('bdist').bdist_base
+ self.bdist_dir = os.path.join(bdist_base, 'egg')
+
+ if self.plat_name is None:
+ self.plat_name = get_build_platform()
+
+ self.set_undefined_options('bdist',('dist_dir', 'dist_dir'))
+
+ if self.egg_output is None:
+
+ # Compute filename of the output egg
+ basename = Distribution(
+ None, None, ei_cmd.egg_name, ei_cmd.egg_version,
+ get_python_version(),
+ self.distribution.has_ext_modules() and self.plat_name
+ ).egg_name()
+
+ self.egg_output = os.path.join(self.dist_dir, basename+'.egg')
+
+
+
+
+
+
+
+
+ def do_install_data(self):
+ # Hack for packages that install data to install's --install-lib
+ self.get_finalized_command('install').install_lib = self.bdist_dir
+
+ site_packages = os.path.normcase(os.path.realpath(get_python_lib()))
+ old, self.distribution.data_files = self.distribution.data_files,[]
+
+ for item in old:
+ if isinstance(item,tuple) and len(item)==2:
+ if os.path.isabs(item[0]):
+ realpath = os.path.realpath(item[0])
+ normalized = os.path.normcase(realpath)
+ if normalized==site_packages or normalized.startswith(
+ site_packages+os.sep
+ ):
+ item = realpath[len(site_packages)+1:], item[1]
+ # XXX else: raise ???
+ self.distribution.data_files.append(item)
+
+ try:
+ log.info("installing package data to %s" % self.bdist_dir)
+ self.call_command('install_data', force=0, root=None)
+ finally:
+ self.distribution.data_files = old
+
+
+ def get_outputs(self):
+ return [self.egg_output]
+
+
+ def call_command(self,cmdname,**kw):
+ """Invoke reinitialized command `cmdname` with keyword args"""
+ for dirname in INSTALL_DIRECTORY_ATTRS:
+ kw.setdefault(dirname,self.bdist_dir)
+ kw.setdefault('skip_build',self.skip_build)
+ kw.setdefault('dry_run', self.dry_run)
+ cmd = self.reinitialize_command(cmdname, **kw)
+ self.run_command(cmdname)
+ return cmd
+
+
+ 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
+ cmd = self.call_command('install_lib', warn_dir=0)
+ instcmd.root = old_root
+
+ all_outputs, ext_outputs = self.get_ext_outputs()
+ self.stubs = []
+ to_compile = []
+ for (p,ext_name) in enumerate(ext_outputs):
+ filename,ext = os.path.splitext(ext_name)
+ pyfile = os.path.join(self.bdist_dir, filename + '.py')
+ self.stubs.append(pyfile)
+ log.info("creating stub loader for %s" % ext_name)
+ if not self.dry_run:
+ write_stub(os.path.basename(ext_name), pyfile)
+ to_compile.append(pyfile)
+ ext_outputs[p] = ext_name.replace(os.sep,'/')
+
+ to_compile.extend(self.make_init_files())
+ if to_compile:
+ cmd.byte_compile(to_compile)
+
+ if self.distribution.data_files:
+ self.do_install_data()
+
+ # Make the EGG-INFO directory
+ archive_root = self.bdist_dir
+ egg_info = os.path.join(archive_root,'EGG-INFO')
+ self.mkpath(egg_info)
+ if self.distribution.scripts:
+ script_dir = os.path.join(egg_info, 'scripts')
+ log.info("installing scripts to %s" % script_dir)
+ self.call_command('install_scripts',install_dir=script_dir,no_ep=1)
+
+ native_libs = os.path.join(self.egg_info,"native_libs.txt")
+ if all_outputs:
+ log.info("writing %s" % native_libs)
+ if not self.dry_run:
+ libs_file = open(native_libs, 'wt')
+ libs_file.write('\n'.join(all_outputs))
+ libs_file.write('\n')
+ libs_file.close()
+ elif os.path.isfile(native_libs):
+ log.info("removing %s" % native_libs)
+ if not self.dry_run:
+ os.unlink(native_libs)
+
+ for filename in os.listdir(self.egg_info):
+ path = os.path.join(self.egg_info,filename)
+ if os.path.isfile(path):
+ self.copy_file(path,os.path.join(egg_info,filename))
+
+ write_safety_flag(
+ os.path.join(archive_root,'EGG-INFO'), self.zip_safe()
+ )
+
+ if os.path.exists(os.path.join(self.egg_info,'depends.txt')):
+ log.warn(
+ "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
+ "Use the install_requires/extras_require setup() args instead."
+ )
+
+ if self.exclude_source_files:
+ self.zap_pyfiles()
+
+ # Make the archive
+ make_zipfile(self.egg_output, archive_root, verbose=self.verbose,
+ dry_run=self.dry_run)
+ if not self.keep_temp:
+ remove_tree(self.bdist_dir, dry_run=self.dry_run)
+
+ # Add to 'Distribution.dist_files' so that the "upload" command works
+ getattr(self.distribution,'dist_files',[]).append(
+ ('bdist_egg',get_python_version(),self.egg_output))
+
+ def zap_pyfiles(self):
+ log.info("Removing .py files from temporary directory")
+ for base,dirs,files in walk_egg(self.bdist_dir):
+ for name in files:
+ if name.endswith('.py'):
+ path = os.path.join(base,name)
+ log.debug("Deleting %s", path)
+ os.unlink(path)
+
+ def zip_safe(self):
+ safe = getattr(self.distribution,'zip_safe',None)
+ if safe is not None:
+ return safe
+ log.warn("zip_safe flag not set; analyzing archive contents...")
+ return analyze_egg(self.bdist_dir, self.stubs)
+
+ def make_init_files(self):
+ """Create missing package __init__ files"""
+ init_files = []
+ for base,dirs,files in walk_egg(self.bdist_dir):
+ if base==self.bdist_dir:
+ # don't put an __init__ in the root
+ continue
+ for name in files:
+ if name.endswith('.py'):
+ if '__init__.py' not in files:
+ pkg = base[len(self.bdist_dir)+1:].replace(os.sep,'.')
+ if self.distribution.has_contents_for(pkg):
+ log.warn("Creating missing __init__.py for %s",pkg)
+ filename = os.path.join(base,'__init__.py')
+ if not self.dry_run:
+ f = open(filename,'w'); f.write(NS_PKG_STUB)
+ f.close()
+ init_files.append(filename)
+ break
+ else:
+ # not a package, don't traverse to subdirectories
+ dirs[:] = []
+
+ return init_files
+
+ def get_ext_outputs(self):
+ """Get a list of relative paths to C extensions in the output distro"""
+
+ all_outputs = []
+ ext_outputs = []
+
+ paths = {self.bdist_dir:''}
+ for base, dirs, files in os.walk(self.bdist_dir):
+ for filename in files:
+ if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
+ all_outputs.append(paths[base]+filename)
+ for filename in dirs:
+ paths[os.path.join(base,filename)] = paths[base]+filename+'/'
+
+ if self.distribution.has_ext_modules():
+ build_cmd = self.get_finalized_command('build_ext')
+ for ext in build_cmd.extensions:
+ if isinstance(ext,Library):
+ continue
+ fullname = build_cmd.get_ext_fullname(ext.name)
+ filename = build_cmd.get_ext_filename(fullname)
+ if not os.path.basename(filename).startswith('dl-'):
+ if os.path.exists(os.path.join(self.bdist_dir,filename)):
+ ext_outputs.append(filename)
+
+ return all_outputs, ext_outputs
+
+
+NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
+
+
+
+
+
+
+
+
+
+
+
+
+def walk_egg(egg_dir):
+ """Walk an unpacked egg's contents, skipping the metadata directory"""
+ walker = os.walk(egg_dir)
+ base,dirs,files = walker.next()
+ if 'EGG-INFO' in dirs:
+ dirs.remove('EGG-INFO')
+ yield base,dirs,files
+ for bdf in walker:
+ yield bdf
+
+def analyze_egg(egg_dir, stubs):
+ # check for existing flag in EGG-INFO
+ for flag,fn in safety_flags.items():
+ if os.path.exists(os.path.join(egg_dir,'EGG-INFO',fn)):
+ return flag
+
+ safe = True
+ for base, dirs, files in walk_egg(egg_dir):
+ for name in files:
+ if name.endswith('.py') or name.endswith('.pyw'):
+ continue
+ elif name.endswith('.pyc') or name.endswith('.pyo'):
+ # always scan, even if we already know we're not safe
+ safe = scan_module(egg_dir, base, name, stubs) and safe
+ return safe
+
+def write_safety_flag(egg_dir, safe):
+ # Write or remove zip safety flag file(s)
+ 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:
+ os.unlink(fn)
+ elif safe is not None and bool(safe)==flag:
+ open(fn,'w').close()
+
+safety_flags = {
+ True: 'zip-safe',
+ False: 'not-zip-safe',
+}
+
+def scan_module(egg_dir, base, name, stubs):
+ """Check whether module possibly uses unsafe-for-zipfile stuff"""
+
+ filename = os.path.join(base,name)
+ if filename[:-1] in stubs:
+ return True # Extension module
+ pkg = base[len(egg_dir)+1:].replace(os.sep,'.')
+ module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0]
+ f = open(filename,'rb'); f.read(8) # skip magic & date
+ code = marshal.load(f); f.close()
+ safe = True
+ symbols = dict.fromkeys(iter_symbols(code))
+ for bad in ['__file__', '__path__']:
+ if bad in symbols:
+ log.warn("%s: module references %s", module, bad)
+ safe = False
+ if 'inspect' in symbols:
+ for bad in [
+ 'getsource', 'getabsfile', 'getsourcefile', 'getfile'
+ 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo',
+ 'getinnerframes', 'getouterframes', 'stack', 'trace'
+ ]:
+ if bad in symbols:
+ log.warn("%s: module MAY be using inspect.%s", module, bad)
+ safe = False
+ if '__name__' in symbols and '__main__' in symbols and '.' not in module:
+ if get_python_version()>="2.4":
+ log.warn("%s: top-level module may be 'python -m' script", module)
+ safe = False
+ return safe
+
+def iter_symbols(code):
+ """Yield names and strings used by `code` and its nested code objects"""
+ for name in code.co_names: yield name
+ for const in code.co_consts:
+ if isinstance(const,basestring):
+ yield const
+ elif isinstance(const,CodeType):
+ for name in iter_symbols(const):
+ yield name
+
+# Attribute names of options for commands that might need to be convinced to
+# install to the egg build directory
+
+INSTALL_DIRECTORY_ATTRS = [
+ 'install_lib', 'install_dir', 'install_data', 'install_base'
+]
+
+def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0, compress=None):
+ """Create a zip file from all the files under 'base_dir'. The output
+ zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
+ Python module (if available) or the InfoZIP "zip" utility (if installed
+ and found on the default search path). If neither tool is available,
+ raises DistutilsExecError. Returns the name of the output zip file.
+ """
+ import zipfile
+ mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
+ log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
+
+ def visit (z, dirname, names):
+ for name in names:
+ path = os.path.normpath(os.path.join(dirname, name))
+ if os.path.isfile(path):
+ p = path[len(base_dir)+1:]
+ if not dry_run:
+ z.write(path, p)
+ log.debug("adding '%s'" % p)
+
+ if compress is None:
+ compress = (sys.version>="2.4") # avoid 2.3 zipimport bug when 64 bits
+
+ compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)]
+ if not dry_run:
+ z = zipfile.ZipFile(zip_filename, "w", compression=compression)
+ os.path.walk(base_dir, visit, z)
+ z.close()
+ else:
+ os.path.walk(base_dir, visit, None)
+
+ return zip_filename
diff --git a/Lib/setuptools/command/bdist_rpm.py b/Lib/setuptools/command/bdist_rpm.py
new file mode 100755
index 0000000..00e07ac
--- /dev/null
+++ b/Lib/setuptools/command/bdist_rpm.py
@@ -0,0 +1,37 @@
+# This is just a kludge so that bdist_rpm doesn't guess wrong about the
+# distribution name and version, if the egg_info command is going to alter
+# them, and another kludge to allow you to build old-style non-egg RPMs
+
+from distutils.command.bdist_rpm import bdist_rpm as _bdist_rpm
+
+class bdist_rpm(_bdist_rpm):
+
+ def initialize_options(self):
+ _bdist_rpm.initialize_options(self)
+ self.no_egg = None
+
+ def run(self):
+ self.run_command('egg_info') # ensure distro name is up-to-date
+ _bdist_rpm.run(self)
+
+ def _make_spec_file(self):
+ version = self.distribution.get_version()
+ rpmversion = version.replace('-','_')
+ spec = _bdist_rpm._make_spec_file(self)
+ line23 = '%define version '+version
+ line24 = '%define version '+rpmversion
+ spec = [
+ line.replace(
+ "Source0: %{name}-%{version}.tar",
+ "Source0: %{name}-%{unmangled_version}.tar"
+ ).replace(
+ "setup.py install ",
+ "setup.py install --single-version-externally-managed "
+ ).replace(
+ "%setup",
+ "%setup -n %{name}-%{unmangled_version}"
+ ).replace(line23,line24)
+ for line in spec
+ ]
+ spec.insert(spec.index(line24)+1, "%define unmangled_version "+version)
+ return spec
diff --git a/Lib/setuptools/command/build_ext.py b/Lib/setuptools/command/build_ext.py
new file mode 100644
index 0000000..f8551fb
--- /dev/null
+++ b/Lib/setuptools/command/build_ext.py
@@ -0,0 +1,285 @@
+from distutils.command.build_ext import build_ext as _du_build_ext
+try:
+ # Attempt to use Pyrex for building extensions, if available
+ from Pyrex.Distutils.build_ext import build_ext as _build_ext
+except ImportError:
+ _build_ext = _du_build_ext
+
+import os, sys
+from distutils.file_util import copy_file
+from setuptools.extension import Library
+from distutils.ccompiler import new_compiler
+from distutils.sysconfig import customize_compiler, get_config_var
+get_config_var("LDSHARED") # make sure _config_vars is initialized
+from distutils.sysconfig import _config_vars
+from distutils import log
+from distutils.errors import *
+
+have_rtld = False
+use_stubs = False
+libtype = 'shared'
+
+if sys.platform == "darwin":
+ use_stubs = True
+elif os.name != 'nt':
+ try:
+ from dl import RTLD_NOW
+ have_rtld = True
+ use_stubs = True
+ except ImportError:
+ pass
+
+def if_dl(s):
+ if have_rtld:
+ return s
+ return ''
+
+
+
+
+
+
+class build_ext(_build_ext):
+ def run(self):
+ """Build extensions in build directory, then copy if --inplace"""
+ old_inplace, self.inplace = self.inplace, 0
+ _build_ext.run(self)
+ self.inplace = old_inplace
+ if old_inplace:
+ self.copy_extensions_to_source()
+
+ def copy_extensions_to_source(self):
+ build_py = self.get_finalized_command('build_py')
+ for ext in self.extensions:
+ fullname = self.get_ext_fullname(ext.name)
+ filename = self.get_ext_filename(fullname)
+ modpath = fullname.split('.')
+ package = '.'.join(modpath[:-1])
+ package_dir = build_py.get_package_dir(package)
+ dest_filename = os.path.join(package_dir,os.path.basename(filename))
+ src_filename = os.path.join(self.build_lib,filename)
+
+ # Always copy, even if source is older than destination, to ensure
+ # that the right extensions for the current Python/platform are
+ # used.
+ copy_file(
+ src_filename, dest_filename, verbose=self.verbose,
+ dry_run=self.dry_run
+ )
+ if ext._needs_stub:
+ self.write_stub(package_dir or os.curdir, ext, True)
+
+
+ if _build_ext is not _du_build_ext:
+ # Workaround for problems using some Pyrex versions w/SWIG and/or 2.4
+ def swig_sources(self, sources, *otherargs):
+ # first do any Pyrex processing
+ sources = _build_ext.swig_sources(self, sources) or sources
+ # Then do any actual SWIG stuff on the remainder
+ return _du_build_ext.swig_sources(self, sources, *otherargs)
+
+
+
+ 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
+
+ def initialize_options(self):
+ _build_ext.initialize_options(self)
+ self.shlib_compiler = None
+ self.shlibs = []
+ self.ext_map = {}
+
+ def finalize_options(self):
+ _build_ext.finalize_options(self)
+ self.extensions = self.extensions or []
+ self.check_extensions_list(self.extensions)
+ self.shlibs = [ext for ext in self.extensions
+ if isinstance(ext,Library)]
+ if self.shlibs:
+ self.setup_shlib_compiler()
+ for ext in self.extensions:
+ fullname = ext._full_name = self.get_ext_fullname(ext.name)
+ self.ext_map[fullname] = ext
+ ltd = ext._links_to_dynamic = \
+ self.shlibs and self.links_to_dynamic(ext) or False
+ ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library)
+ filename = ext._file_name = self.get_ext_filename(fullname)
+ libdir = os.path.dirname(os.path.join(self.build_lib,filename))
+ if ltd and libdir not in ext.library_dirs:
+ ext.library_dirs.append(libdir)
+ if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs:
+ ext.runtime_library_dirs.append(os.curdir)
+
+
+
+ def setup_shlib_compiler(self):
+ compiler = self.shlib_compiler = new_compiler(
+ compiler=self.compiler, dry_run=self.dry_run, force=self.force
+ )
+ if sys.platform == "darwin":
+ tmp = _config_vars.copy()
+ try:
+ # XXX Help! I don't have any idea whether these are right...
+ _config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup"
+ _config_vars['CCSHARED'] = " -dynamiclib"
+ _config_vars['SO'] = ".dylib"
+ customize_compiler(compiler)
+ finally:
+ _config_vars.clear()
+ _config_vars.update(tmp)
+ else:
+ customize_compiler(compiler)
+
+ if self.include_dirs is not None:
+ compiler.set_include_dirs(self.include_dirs)
+ if self.define is not None:
+ # 'define' option is a list of (name,value) tuples
+ for (name,value) in self.define:
+ compiler.define_macro(name, value)
+ if self.undef is not None:
+ for macro in self.undef:
+ compiler.undefine_macro(macro)
+ if self.libraries is not None:
+ compiler.set_libraries(self.libraries)
+ if self.library_dirs is not None:
+ compiler.set_library_dirs(self.library_dirs)
+ if self.rpath is not None:
+ compiler.set_runtime_library_dirs(self.rpath)
+ if self.link_objects is not None:
+ compiler.set_link_objects(self.link_objects)
+
+ # hack so distutils' build_extension() builds a library instead
+ compiler.link_shared_object = link_shared_object.__get__(compiler)
+
+
+
+ def get_export_symbols(self, ext):
+ if isinstance(ext,Library):
+ return ext.export_symbols
+ return _build_ext.get_export_symbols(self,ext)
+
+ def build_extension(self, ext):
+ _compiler = self.compiler
+ try:
+ if isinstance(ext,Library):
+ self.compiler = self.shlib_compiler
+ _build_ext.build_extension(self,ext)
+ if ext._needs_stub:
+ self.write_stub(
+ self.get_finalized_command('build_py').build_lib, ext
+ )
+ finally:
+ self.compiler = _compiler
+
+ def links_to_dynamic(self, ext):
+ """Return true if 'ext' links to a dynamic lib in the same package"""
+ # XXX this should check to ensure the lib is actually being built
+ # XXX as dynamic, and not just using a locally-found version or a
+ # XXX static-compiled version
+ libnames = dict.fromkeys([lib._full_name for lib in self.shlibs])
+ pkg = '.'.join(ext._full_name.split('.')[:-1]+[''])
+ for libname in ext.libraries:
+ if pkg+libname in libnames: return True
+ return False
+
+ def get_outputs(self):
+ outputs = _build_ext.get_outputs(self)
+ optimize = self.get_finalized_command('build_py').optimize
+ for ext in self.extensions:
+ if ext._needs_stub:
+ base = os.path.join(self.build_lib, *ext._full_name.split('.'))
+ outputs.append(base+'.py')
+ outputs.append(base+'.pyc')
+ if optimize:
+ outputs.append(base+'.pyo')
+ return outputs
+
+ def write_stub(self, output_dir, ext, compile=False):
+ log.info("writing stub loader for %s to %s",ext._full_name, output_dir)
+ stub_file = os.path.join(output_dir, *ext._full_name.split('.'))+'.py'
+ if compile and os.path.exists(stub_file):
+ raise DistutilsError(stub_file+" already exists! Please delete.")
+ if not self.dry_run:
+ f = open(stub_file,'w')
+ f.write('\n'.join([
+ "def __bootstrap__():",
+ " global __bootstrap__, __file__, __loader__",
+ " import sys, os, pkg_resources, imp"+if_dl(", dl"),
+ " __file__ = pkg_resources.resource_filename(__name__,%r)"
+ % os.path.basename(ext._file_name),
+ " del __bootstrap__",
+ " if '__loader__' in globals():",
+ " del __loader__",
+ if_dl(" old_flags = sys.getdlopenflags()"),
+ " old_dir = os.getcwd()",
+ " try:",
+ " os.chdir(os.path.dirname(__file__))",
+ if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
+ " imp.load_dynamic(__name__,__file__)",
+ " finally:",
+ if_dl(" sys.setdlopenflags(old_flags)"),
+ " os.chdir(old_dir)",
+ "__bootstrap__()",
+ "" # terminal \n
+ ]))
+ f.close()
+ if compile:
+ from distutils.util import byte_compile
+ byte_compile([stub_file], optimize=0,
+ force=True, dry_run=self.dry_run)
+ optimize = self.get_finalized_command('install_lib').optimize
+ if optimize > 0:
+ byte_compile([stub_file], optimize=optimize,
+ force=True, dry_run=self.dry_run)
+ if os.path.exists(stub_file) and not self.dry_run:
+ os.unlink(stub_file)
+
+
+if use_stubs or os.name=='nt':
+ # Build shared libraries
+ #
+ def link_shared_object(self, objects, output_libname, output_dir=None,
+ libraries=None, library_dirs=None, runtime_library_dirs=None,
+ export_symbols=None, debug=0, extra_preargs=None,
+ extra_postargs=None, build_temp=None, target_lang=None
+ ): self.link(
+ self.SHARED_LIBRARY, objects, output_libname,
+ output_dir, libraries, library_dirs, runtime_library_dirs,
+ export_symbols, debug, extra_preargs, extra_postargs,
+ build_temp, target_lang
+ )
+else:
+ # Build static libraries everywhere else
+ libtype = 'static'
+
+ def link_shared_object(self, objects, output_libname, output_dir=None,
+ libraries=None, library_dirs=None, runtime_library_dirs=None,
+ export_symbols=None, debug=0, extra_preargs=None,
+ extra_postargs=None, build_temp=None, target_lang=None
+ ):
+ # XXX we need to either disallow these attrs on Library instances,
+ # or warn/abort here if set, or something...
+ #libraries=None, library_dirs=None, runtime_library_dirs=None,
+ #export_symbols=None, extra_preargs=None, extra_postargs=None,
+ #build_temp=None
+
+ assert output_dir is None # distutils build_ext doesn't pass this
+ output_dir,filename = os.path.split(output_libname)
+ basename, ext = os.path.splitext(filename)
+ if self.library_filename("x").startswith('lib'):
+ # strip 'lib' prefix; this is kludgy if some platform uses
+ # a different prefix
+ basename = basename[3:]
+
+ self.create_static_lib(
+ objects, basename, output_dir, debug, target_lang
+ )
diff --git a/Lib/setuptools/command/build_py.py b/Lib/setuptools/command/build_py.py
new file mode 100644
index 0000000..77a9b23
--- /dev/null
+++ b/Lib/setuptools/command/build_py.py
@@ -0,0 +1,192 @@
+import os.path, sys, fnmatch
+from distutils.command.build_py import build_py as _build_py
+from distutils.util import convert_path
+from glob import glob
+
+class build_py(_build_py):
+ """Enhanced 'build_py' command that includes data files with packages
+
+ The data files are specified via a 'package_data' argument to 'setup()'.
+ See 'setuptools.dist.Distribution' for more details.
+
+ Also, this version of the 'build_py' command allows you to specify both
+ 'py_modules' and 'packages' in the same setup operation.
+ """
+ def finalize_options(self):
+ _build_py.finalize_options(self)
+ self.package_data = self.distribution.package_data
+ self.exclude_package_data = self.distribution.exclude_package_data or {}
+ if 'data_files' in self.__dict__: del self.__dict__['data_files']
+
+ def run(self):
+ """Build modules, packages, and copy data files to build directory"""
+ if not self.py_modules and not self.packages:
+ return
+
+ if self.py_modules:
+ self.build_modules()
+
+ if self.packages:
+ self.build_packages()
+ self.build_package_data()
+
+ # Only compile actual .py files, using our base class' idea of what our
+ # output files are.
+ self.byte_compile(_build_py.get_outputs(self, include_bytecode=0))
+
+ def __getattr__(self,attr):
+ if attr=='data_files': # lazily compute data files
+ self.data_files = files = self._get_data_files(); return files
+ return _build_py.__getattr__(self,attr)
+
+ def _get_data_files(self):
+ """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
+ self.analyze_manifest()
+ data = []
+ for package in self.packages or ():
+ # Locate package source directory
+ src_dir = self.get_package_dir(package)
+
+ # Compute package build directory
+ build_dir = os.path.join(*([self.build_lib] + package.split('.')))
+
+ # Length of path to strip from found files
+ plen = len(src_dir)+1
+
+ # Strip directory from globbed filenames
+ filenames = [
+ file[plen:] for file in self.find_data_files(package, src_dir)
+ ]
+ data.append( (package, src_dir, build_dir, filenames) )
+ return data
+
+ def find_data_files(self, package, src_dir):
+ """Return filenames for package's data files in 'src_dir'"""
+ globs = (self.package_data.get('', [])
+ + self.package_data.get(package, []))
+ files = self.manifest_files.get(package, [])[:]
+ for pattern in globs:
+ # Each pattern has to be converted to a platform-specific path
+ files.extend(glob(os.path.join(src_dir, convert_path(pattern))))
+ return self.exclude_data_files(package, src_dir, files)
+
+ def build_package_data(self):
+ """Copy data files into build directory"""
+ lastdir = None
+ for package, src_dir, build_dir, filenames in self.data_files:
+ for filename in filenames:
+ target = os.path.join(build_dir, filename)
+ self.mkpath(os.path.dirname(target))
+ self.copy_file(os.path.join(src_dir, filename), target)
+
+
+ def analyze_manifest(self):
+ self.manifest_files = mf = {}
+ if not self.distribution.include_package_data:
+ return
+ src_dirs = {}
+ for package in self.packages or ():
+ # Locate package source directory
+ src_dirs[assert_relative(self.get_package_dir(package))] = package
+
+ self.run_command('egg_info')
+ ei_cmd = self.get_finalized_command('egg_info')
+ for path in ei_cmd.filelist.files:
+ if path.endswith('.py'):
+ continue
+ d,f = os.path.split(assert_relative(path))
+ prev = None
+ while d and d!=prev and d not in src_dirs:
+ prev = d
+ d, df = os.path.split(d)
+ f = os.path.join(df, f)
+ if d in src_dirs:
+ mf.setdefault(src_dirs[d],[]).append(path)
+
+
+ def get_data_files(self): pass # kludge 2.4 for lazy computation
+
+ if sys.version<"2.4": # Python 2.4 already has this code
+ def get_outputs(self, include_bytecode=1):
+ """Return complete list of files copied to the build directory
+
+ This includes both '.py' files and data files, as well as '.pyc'
+ and '.pyo' files if 'include_bytecode' is true. (This method is
+ needed for the 'install_lib' command to do its job properly, and to
+ generate a correct installation manifest.)
+ """
+ return _build_py.get_outputs(self, include_bytecode) + [
+ os.path.join(build_dir, filename)
+ for package, src_dir, build_dir,filenames in self.data_files
+ for filename in filenames
+ ]
+
+ def check_package(self, package, package_dir):
+ """Check namespace packages' __init__ for declare_namespace"""
+ try:
+ return self.packages_checked[package]
+ except KeyError:
+ pass
+
+ init_py = _build_py.check_package(self, package, package_dir)
+ self.packages_checked[package] = init_py
+
+ if not init_py or not self.distribution.namespace_packages:
+ return init_py
+
+ for pkg in self.distribution.namespace_packages:
+ if pkg==package or pkg.startswith(package+'.'):
+ break
+ else:
+ return init_py
+
+ f = open(init_py,'rU')
+ if 'declare_namespace' not in f.read():
+ from distutils.errors import DistutilsError
+ raise DistutilsError(
+ "Namespace package problem: %s is a namespace package, but its\n"
+ "__init__.py does not call declare_namespace()! Please fix it.\n"
+ '(See the setuptools manual under "Namespace Packages" for '
+ "details.)\n" % (package,)
+ )
+ f.close()
+ return init_py
+
+ def initialize_options(self):
+ self.packages_checked={}
+ _build_py.initialize_options(self)
+
+
+
+
+
+
+
+ def exclude_data_files(self, package, src_dir, files):
+ """Filter filenames for package's data files in 'src_dir'"""
+ globs = (self.exclude_package_data.get('', [])
+ + self.exclude_package_data.get(package, []))
+ bad = []
+ for pattern in globs:
+ bad.extend(
+ fnmatch.filter(
+ files, os.path.join(src_dir, convert_path(pattern))
+ )
+ )
+ bad = dict.fromkeys(bad)
+ return [f for f in files if f not in bad]
+
+
+def assert_relative(path):
+ if not os.path.isabs(path):
+ return path
+ from distutils.errors import DistutilsSetupError
+ raise DistutilsSetupError(
+"""Error: setup script specifies an absolute path:
+
+ %s
+
+setup() arguments must *always* be /-separated paths relative to the
+setup.py directory, *never* absolute paths.
+""" % path
+ )
diff --git a/Lib/setuptools/command/develop.py b/Lib/setuptools/command/develop.py
new file mode 100755
index 0000000..7ab5b23
--- /dev/null
+++ b/Lib/setuptools/command/develop.py
@@ -0,0 +1,116 @@
+from setuptools.command.easy_install import easy_install
+from distutils.util import convert_path
+from pkg_resources import Distribution, PathMetadata, normalize_path
+from distutils import log
+from distutils.errors import *
+import sys, os
+
+class develop(easy_install):
+ """Set up package for development"""
+
+ description = "install package in 'development mode'"
+
+ user_options = easy_install.user_options + [
+ ("uninstall", "u", "Uninstall this source package"),
+ ]
+
+ boolean_options = easy_install.boolean_options + ['uninstall']
+
+ command_consumes_arguments = False # override base
+
+ def run(self):
+ if self.uninstall:
+ self.multi_version = True
+ self.uninstall_link()
+ else:
+ self.install_for_development()
+ self.warn_deprecated_options()
+
+ def initialize_options(self):
+ self.uninstall = None
+ easy_install.initialize_options(self)
+
+
+
+
+
+
+
+
+
+
+ def finalize_options(self):
+ ei = self.get_finalized_command("egg_info")
+ if ei.broken_egg_info:
+ raise DistutilsError(
+ "Please rename %r to %r before using 'develop'"
+ % (ei.egg_info, ei.broken_egg_info)
+ )
+ self.args = [ei.egg_name]
+ easy_install.finalize_options(self)
+ self.egg_link = os.path.join(self.install_dir, ei.egg_name+'.egg-link')
+ self.egg_base = ei.egg_base
+ self.egg_path = os.path.abspath(ei.egg_base)
+
+ # Make a distribution for the package's source
+ self.dist = Distribution(
+ normalize_path(self.egg_path),
+ PathMetadata(self.egg_path, os.path.abspath(ei.egg_info)),
+ project_name = ei.egg_name
+ )
+
+ def install_for_development(self):
+ # Ensure metadata is up-to-date
+ self.run_command('egg_info')
+
+ # Build extensions in-place
+ self.reinitialize_command('build_ext', inplace=1)
+ self.run_command('build_ext')
+
+ self.install_site_py() # ensure that target dir is site-safe
+
+ # create an .egg-link in the installation dir, pointing to our egg
+ log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
+ if not self.dry_run:
+ f = open(self.egg_link,"w")
+ f.write(self.egg_path)
+ f.close()
+
+ # postprocess the installed distro, fixing up .pth, installing scripts,
+ # and handling requirements
+ self.process_distribution(None, self.dist)
+
+ def uninstall_link(self):
+ if os.path.exists(self.egg_link):
+ log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
+ contents = [line.rstrip() for line in file(self.egg_link)]
+ if contents != [self.egg_path]:
+ log.warn("Link points to %s: uninstall aborted", contents)
+ return
+ if not self.dry_run:
+ os.unlink(self.egg_link)
+ if not self.dry_run:
+ self.update_pth(self.dist) # remove any .pth link to us
+ if self.distribution.scripts:
+ # XXX should also check for entry point scripts!
+ log.warn("Note: you must uninstall or replace scripts manually!")
+
+
+ def install_egg_scripts(self, dist):
+ if dist is not self.dist:
+ # Installing a dependency, so fall back to normal behavior
+ return easy_install.install_egg_scripts(self,dist)
+
+ # create wrapper scripts in the script dir, pointing to dist.scripts
+
+ # new-style...
+ self.install_wrapper_scripts(dist)
+
+ # ...and old-style
+ for script_name in self.distribution.scripts or []:
+ script_path = os.path.abspath(convert_path(script_name))
+ script_name = os.path.basename(script_path)
+ f = open(script_path,'rU')
+ script_text = f.read()
+ f.close()
+ self.install_script(dist, script_name, script_text, script_path)
diff --git a/Lib/setuptools/command/easy_install.py b/Lib/setuptools/command/easy_install.py
new file mode 100755
index 0000000..3ddcec4
--- /dev/null
+++ b/Lib/setuptools/command/easy_install.py
@@ -0,0 +1,1555 @@
+#!python
+"""\
+Easy Install
+------------
+
+A tool for doing automatic download/extract/build of distutils-based Python
+packages. For detailed documentation, see the accompanying EasyInstall.txt
+file, or visit the `EasyInstall home page`__.
+
+__ http://peak.telecommunity.com/DevCenter/EasyInstall
+"""
+import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random
+from glob import glob
+from setuptools import Command
+from setuptools.sandbox import run_setup
+from distutils import log, dir_util
+from distutils.sysconfig import get_python_lib
+from distutils.errors import DistutilsArgError, DistutilsOptionError, \
+ DistutilsError
+from setuptools.archive_util import unpack_archive
+from setuptools.package_index import PackageIndex, parse_bdist_wininst
+from setuptools.package_index import URL_SCHEME
+from setuptools.command import bdist_egg, egg_info
+from pkg_resources import *
+sys_executable = os.path.normpath(sys.executable)
+
+__all__ = [
+ 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
+ 'main', 'get_exe_prefixes',
+]
+
+def samefile(p1,p2):
+ if hasattr(os.path,'samefile') and (
+ os.path.exists(p1) and os.path.exists(p2)
+ ):
+ return os.path.samefile(p1,p2)
+ return (
+ os.path.normpath(os.path.normcase(p1)) ==
+ os.path.normpath(os.path.normcase(p2))
+ )
+
+class easy_install(Command):
+ """Manage a download/build/install process"""
+ description = "Find/get/install Python packages"
+ command_consumes_arguments = True
+
+ user_options = [
+ ('prefix=', None, "installation prefix"),
+ ("zip-ok", "z", "install package as a zipfile"),
+ ("multi-version", "m", "make apps have to require() a version"),
+ ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"),
+ ("install-dir=", "d", "install package to DIR"),
+ ("script-dir=", "s", "install scripts to DIR"),
+ ("exclude-scripts", "x", "Don't install scripts"),
+ ("always-copy", "a", "Copy all needed packages to install dir"),
+ ("index-url=", "i", "base URL of Python Package Index"),
+ ("find-links=", "f", "additional URL(s) to search for packages"),
+ ("delete-conflicting", "D", "no longer needed; don't use this"),
+ ("ignore-conflicts-at-my-risk", None,
+ "no longer needed; don't use this"),
+ ("build-directory=", "b",
+ "download/extract/build in DIR; keep the results"),
+ ('optimize=', 'O',
+ "also compile with optimization: -O1 for \"python -O\", "
+ "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
+ ('record=', None,
+ "filename in which to record list of installed files"),
+ ('always-unzip', 'Z', "don't install as a zipfile, no matter what"),
+ ('site-dirs=','S',"list of directories where .pth files work"),
+ ('editable', 'e', "Install specified packages in editable form"),
+ ('no-deps', 'N', "don't install dependencies"),
+ ('allow-hosts=', 'H', "pattern(s) that hostnames must match"),
+ ]
+ boolean_options = [
+ 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',
+ 'delete-conflicting', 'ignore-conflicts-at-my-risk', 'editable',
+ 'no-deps',
+ ]
+ negative_opt = {'always-unzip': 'zip-ok'}
+ create_index = PackageIndex
+
+
+ def initialize_options(self):
+ self.zip_ok = None
+ self.install_dir = self.script_dir = self.exclude_scripts = None
+ self.index_url = None
+ self.find_links = None
+ self.build_directory = None
+ self.args = None
+ self.optimize = self.record = None
+ self.upgrade = self.always_copy = self.multi_version = None
+ self.editable = self.no_deps = self.allow_hosts = None
+ self.root = self.prefix = self.no_report = None
+
+ # Options not specifiable via command line
+ self.package_index = None
+ self.pth_file = None
+ self.delete_conflicting = None
+ self.ignore_conflicts_at_my_risk = None
+ self.site_dirs = None
+ self.installed_projects = {}
+ self.sitepy_installed = False
+ # Always read easy_install options, even if we are subclassed, or have
+ # an independent instance created. This ensures that defaults will
+ # always come from the standard configuration file(s)' "easy_install"
+ # section, even if this is a "develop" or "install" command, or some
+ # other embedding.
+ self._dry_run = None
+ self.verbose = self.distribution.verbose
+ self.distribution._set_command_options(
+ self, self.distribution.get_option_dict('easy_install')
+ )
+
+ def delete_blockers(self, blockers):
+ for filename in blockers:
+ if os.path.exists(filename) or os.path.islink(filename):
+ log.info("Deleting %s", filename)
+ if not self.dry_run:
+ if os.path.isdir(filename) and not os.path.islink(filename):
+ rmtree(filename)
+ else:
+ os.unlink(filename)
+
+ def finalize_options(self):
+ self._expand('install_dir','script_dir','build_directory','site_dirs')
+ # If a non-default installation directory was specified, default the
+ # script directory to match it.
+ if self.script_dir is None:
+ self.script_dir = self.install_dir
+
+ # Let install_dir get set by install_lib command, which in turn
+ # gets its info from the install command, and takes into account
+ # --prefix and --home and all that other crud.
+ self.set_undefined_options('install_lib',
+ ('install_dir','install_dir')
+ )
+ # Likewise, set default script_dir from 'install_scripts.install_dir'
+ self.set_undefined_options('install_scripts',
+ ('install_dir', 'script_dir')
+ )
+ # default --record from the install command
+ self.set_undefined_options('install', ('record', 'record'))
+ normpath = map(normalize_path, sys.path)
+ self.all_site_dirs = get_site_dirs()
+ if self.site_dirs is not None:
+ site_dirs = [
+ os.path.expanduser(s.strip()) for s in self.site_dirs.split(',')
+ ]
+ for d in site_dirs:
+ if not os.path.isdir(d):
+ log.warn("%s (in --site-dirs) does not exist", d)
+ elif normalize_path(d) not in normpath:
+ raise DistutilsOptionError(
+ d+" (in --site-dirs) is not on sys.path"
+ )
+ else:
+ self.all_site_dirs.append(normalize_path(d))
+ self.check_site_dir()
+ self.index_url = self.index_url or "http://www.python.org/pypi"
+ self.shadow_path = self.all_site_dirs[:]
+ for path_item in self.install_dir, normalize_path(self.script_dir):
+ if path_item not in self.shadow_path:
+ self.shadow_path.insert(0, path_item)
+
+ if self.allow_hosts is not None:
+ hosts = [s.strip() for s in self.allow_hosts.split(',')]
+ else:
+ hosts = ['*']
+
+ if self.package_index is None:
+ self.package_index = self.create_index(
+ self.index_url, search_path = self.shadow_path, hosts=hosts
+ )
+ self.local_index = Environment(self.shadow_path)
+
+ if self.find_links is not None:
+ if isinstance(self.find_links, basestring):
+ self.find_links = self.find_links.split()
+ else:
+ self.find_links = []
+
+ self.package_index.add_find_links(self.find_links)
+ self.set_undefined_options('install_lib', ('optimize','optimize'))
+ if not isinstance(self.optimize,int):
+ try:
+ self.optimize = int(self.optimize)
+ if not (0 <= self.optimize <= 2): raise ValueError
+ except ValueError:
+ raise DistutilsOptionError("--optimize must be 0, 1, or 2")
+
+ if self.delete_conflicting and self.ignore_conflicts_at_my_risk:
+ raise DistutilsOptionError(
+ "Can't use both --delete-conflicting and "
+ "--ignore-conflicts-at-my-risk at the same time"
+ )
+ if self.editable and not self.build_directory:
+ raise DistutilsArgError(
+ "Must specify a build directory (-b) when using --editable"
+ )
+ if not self.args:
+ raise DistutilsArgError(
+ "No urls, filenames, or requirements specified (see --help)")
+
+ self.outputs = []
+
+ def run(self):
+ if self.verbose<>self.distribution.verbose:
+ log.set_verbosity(self.verbose)
+ try:
+ for spec in self.args:
+ self.easy_install(spec, not self.no_deps)
+ if self.record:
+ outputs = self.outputs
+ if self.root: # strip any package prefix
+ root_len = len(self.root)
+ for counter in xrange(len(outputs)):
+ outputs[counter] = outputs[counter][root_len:]
+ from distutils import file_util
+ self.execute(
+ file_util.write_file, (self.record, outputs),
+ "writing list of installed files to '%s'" %
+ self.record
+ )
+ self.warn_deprecated_options()
+ finally:
+ log.set_verbosity(self.distribution.verbose)
+
+ def pseudo_tempname(self):
+ """Return a pseudo-tempname base in the install directory.
+ This code is intentionally naive; if a malicious party can write to
+ the target directory you're already in deep doodoo.
+ """
+ try:
+ pid = os.getpid()
+ except:
+ pid = random.randint(0,sys.maxint)
+ return os.path.join(self.install_dir, "test-easy-install-%s" % pid)
+
+ def warn_deprecated_options(self):
+ if self.delete_conflicting or self.ignore_conflicts_at_my_risk:
+ log.warn(
+ "Note: The -D, --delete-conflicting and"
+ " --ignore-conflicts-at-my-risk no longer have any purpose"
+ " and should not be used."
+ )
+
+ def check_site_dir(self):
+ """Verify that self.install_dir is .pth-capable dir, if needed"""
+
+ instdir = normalize_path(self.install_dir)
+ pth_file = os.path.join(instdir,'easy-install.pth')
+
+ # Is it a configured, PYTHONPATH, implicit, or explicit site dir?
+ is_site_dir = instdir in self.all_site_dirs
+
+ if not is_site_dir:
+ # No? Then directly test whether it does .pth file processing
+ is_site_dir = self.check_pth_processing()
+ else:
+ # make sure we can write to target dir
+ testfile = self.pseudo_tempname()+'.write-test'
+ test_exists = os.path.exists(testfile)
+ try:
+ if test_exists: os.unlink(testfile)
+ open(testfile,'w').close()
+ os.unlink(testfile)
+ except (OSError,IOError):
+ self.cant_write_to_target()
+
+ if not is_site_dir and not self.multi_version:
+ # Can't install non-multi to non-site dir
+ raise DistutilsError(self.no_default_version_msg())
+
+ if is_site_dir:
+ if self.pth_file is None:
+ self.pth_file = PthDistributions(pth_file)
+ else:
+ self.pth_file = None
+
+ PYTHONPATH = os.environ.get('PYTHONPATH','').split(os.pathsep)
+ if instdir not in map(normalize_path, filter(None,PYTHONPATH)):
+ # only PYTHONPATH dirs need a site.py, so pretend it's there
+ self.sitepy_installed = True
+
+ self.install_dir = instdir
+
+
+ def cant_write_to_target(self):
+ msg = """can't create or remove files in install directory
+
+The following error occurred while trying to add or remove files in the
+installation directory:
+
+ %s
+
+The installation directory you specified (via --install-dir, --prefix, or
+the distutils default setting) was:
+
+ %s
+""" % (sys.exc_info()[1], self.install_dir,)
+
+ if not os.path.exists(self.install_dir):
+ msg += """
+This directory does not currently exist. Please create it and try again, or
+choose a different installation directory (using the -d or --install-dir
+option).
+"""
+ else:
+ msg += """
+Perhaps your account does not have write access to this directory? If the
+installation directory is a system-owned directory, you may need to sign in
+as the administrator or "root" account. If you do not have administrative
+access to this machine, you may wish to choose a different installation
+directory, preferably one that is listed in your PYTHONPATH environment
+variable.
+
+For information on other options, you may wish to consult the
+documentation at:
+
+ http://peak.telecommunity.com/EasyInstall.html
+
+Please make the appropriate changes for your system and try again.
+"""
+ raise DistutilsError(msg)
+
+
+
+
+ def check_pth_processing(self):
+ """Empirically verify whether .pth files are supported in inst. dir"""
+ instdir = self.install_dir
+ log.info("Checking .pth file support in %s", instdir)
+ pth_file = self.pseudo_tempname()+".pth"
+ ok_file = pth_file+'.ok'
+ ok_exists = os.path.exists(ok_file)
+ try:
+ if ok_exists: os.unlink(ok_file)
+ f = open(pth_file,'w')
+ except (OSError,IOError):
+ self.cant_write_to_target()
+ else:
+ try:
+ f.write("import os;open(%r,'w').write('OK')\n" % (ok_file,))
+ f.close(); f=None
+ executable = sys.executable
+ if os.name=='nt':
+ dirname,basename = os.path.split(executable)
+ alt = os.path.join(dirname,'pythonw.exe')
+ if basename.lower()=='python.exe' and os.path.exists(alt):
+ # use pythonw.exe to avoid opening a console window
+ executable = alt
+ if ' ' in executable: executable='"%s"' % executable
+ from distutils.spawn import spawn
+ spawn([executable,'-E','-c','pass'],0)
+
+ if os.path.exists(ok_file):
+ log.info(
+ "TEST PASSED: %s appears to support .pth files",
+ instdir
+ )
+ return True
+ finally:
+ if f: f.close()
+ if os.path.exists(ok_file): os.unlink(ok_file)
+ if os.path.exists(pth_file): os.unlink(pth_file)
+ if not self.multi_version:
+ log.warn("TEST FAILED: %s does NOT support .pth files", instdir)
+ return False
+
+ def install_egg_scripts(self, dist):
+ """Write all the scripts for `dist`, unless scripts are excluded"""
+
+ self.install_wrapper_scripts(dist)
+ if self.exclude_scripts or not dist.metadata_isdir('scripts'):
+ return
+
+ for script_name in dist.metadata_listdir('scripts'):
+ self.install_script(
+ dist, script_name,
+ dist.get_metadata('scripts/'+script_name).replace('\r','\n')
+ )
+
+ def add_output(self, path):
+ if os.path.isdir(path):
+ for base, dirs, files in os.walk(path):
+ for filename in files:
+ self.outputs.append(os.path.join(base,filename))
+ else:
+ self.outputs.append(path)
+
+ def not_editable(self, spec):
+ if self.editable:
+ raise DistutilsArgError(
+ "Invalid argument %r: you can't use filenames or URLs "
+ "with --editable (except via the --find-links option)."
+ % (spec,)
+ )
+
+ def check_editable(self,spec):
+ if not self.editable:
+ return
+
+ if os.path.exists(os.path.join(self.build_directory, spec.key)):
+ raise DistutilsArgError(
+ "%r already exists in %s; can't do a checkout there" %
+ (spec.key, self.build_directory)
+ )
+
+
+
+ def easy_install(self, spec, deps=False):
+ tmpdir = tempfile.mkdtemp(prefix="easy_install-")
+ download = None
+ self.install_site_py()
+
+ try:
+ if not isinstance(spec,Requirement):
+ if URL_SCHEME(spec):
+ # It's a url, download it to tmpdir and process
+ self.not_editable(spec)
+ download = self.package_index.download(spec, tmpdir)
+ return self.install_item(None, download, tmpdir, deps, True)
+
+ elif os.path.exists(spec):
+ # Existing file or directory, just process it directly
+ self.not_editable(spec)
+ return self.install_item(None, spec, tmpdir, deps, True)
+ else:
+ spec = parse_requirement_arg(spec)
+
+ self.check_editable(spec)
+ dist = self.package_index.fetch_distribution(
+ spec, tmpdir, self.upgrade, self.editable, not self.always_copy
+ )
+
+ if dist is None:
+ msg = "Could not find suitable distribution for %r" % spec
+ if self.always_copy:
+ msg+=" (--always-copy skips system and development eggs)"
+ raise DistutilsError(msg)
+ elif dist.precedence==DEVELOP_DIST:
+ # .egg-info dists don't need installing, just process deps
+ self.process_distribution(spec, dist, deps, "Using")
+ return dist
+ else:
+ return self.install_item(spec, dist.location, tmpdir, deps)
+
+ finally:
+ if os.path.exists(tmpdir):
+ rmtree(tmpdir)
+
+ def install_item(self, spec, download, tmpdir, deps, install_needed=False):
+
+ # Installation is also needed if file in tmpdir or is not an egg
+ install_needed = install_needed or os.path.dirname(download) == tmpdir
+ install_needed = install_needed or not download.endswith('.egg')
+
+ log.info("Processing %s", os.path.basename(download))
+
+ if install_needed or self.always_copy:
+ dists = self.install_eggs(spec, download, tmpdir)
+ for dist in dists:
+ self.process_distribution(spec, dist, deps)
+ else:
+ dists = [self.check_conflicts(self.egg_distribution(download))]
+ self.process_distribution(spec, dists[0], deps, "Using")
+
+ if spec is not None:
+ for dist in dists:
+ if dist in spec:
+ return dist
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ def process_distribution(self, requirement, dist, deps=True, *info):
+ self.update_pth(dist)
+ self.package_index.add(dist)
+ self.local_index.add(dist)
+ self.install_egg_scripts(dist)
+ self.installed_projects[dist.key] = dist
+ log.warn(self.installation_report(requirement, dist, *info))
+ if not deps and not self.always_copy:
+ return
+ elif requirement is not None and dist.key != requirement.key:
+ log.warn("Skipping dependencies for %s", dist)
+ return # XXX this is not the distribution we were looking for
+ elif requirement is None or dist not in requirement:
+ # if we wound up with a different version, resolve what we've got
+ distreq = dist.as_requirement()
+ requirement = requirement or distreq
+ requirement = Requirement(
+ distreq.project_name, distreq.specs, requirement.extras
+ )
+ if dist.has_metadata('dependency_links.txt'):
+ self.package_index.add_find_links(
+ dist.get_metadata_lines('dependency_links.txt')
+ )
+ log.info("Processing dependencies for %s", requirement)
+ try:
+ distros = WorkingSet([]).resolve(
+ [requirement], self.local_index, self.easy_install
+ )
+ except DistributionNotFound, e:
+ raise DistutilsError(
+ "Could not find required distribution %s" % e.args
+ )
+ except VersionConflict, e:
+ raise DistutilsError(
+ "Installed distribution %s conflicts with requirement %s"
+ % e.args
+ )
+ if self.always_copy:
+ # Force all the relevant distros to be copied or activated
+ for dist in distros:
+ if dist.key not in self.installed_projects:
+ self.easy_install(dist.as_requirement())
+
+ def should_unzip(self, dist):
+ if self.zip_ok is not None:
+ return not self.zip_ok
+ if dist.has_metadata('not-zip-safe'):
+ return True
+ if not dist.has_metadata('zip-safe'):
+ return True
+ return False
+
+ def maybe_move(self, spec, dist_filename, setup_base):
+ dst = os.path.join(self.build_directory, spec.key)
+ if os.path.exists(dst):
+ log.warn(
+ "%r already exists in %s; build directory %s will not be kept",
+ spec.key, self.build_directory, setup_base
+ )
+ return setup_base
+ if os.path.isdir(dist_filename):
+ setup_base = dist_filename
+ else:
+ if os.path.dirname(dist_filename)==setup_base:
+ os.unlink(dist_filename) # get it out of the tmp dir
+ contents = os.listdir(setup_base)
+ if len(contents)==1:
+ dist_filename = os.path.join(setup_base,contents[0])
+ if os.path.isdir(dist_filename):
+ # if the only thing there is a directory, move it instead
+ setup_base = dist_filename
+ ensure_directory(dst); shutil.move(setup_base, dst)
+ return dst
+
+ def install_wrapper_scripts(self, dist):
+ if not self.exclude_scripts:
+ for args in get_script_args(dist):
+ self.write_script(*args)
+
+
+
+
+
+
+ def install_script(self, dist, script_name, script_text, dev_path=None):
+ """Generate a legacy script wrapper and install it"""
+ spec = str(dist.as_requirement())
+
+ if dev_path:
+ script_text = get_script_header(script_text) + (
+ "# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r\n"
+ "__requires__ = %(spec)r\n"
+ "from pkg_resources import require; require(%(spec)r)\n"
+ "del require\n"
+ "__file__ = %(dev_path)r\n"
+ "execfile(__file__)\n"
+ ) % locals()
+ else:
+ script_text = get_script_header(script_text) + (
+ "# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r\n"
+ "__requires__ = %(spec)r\n"
+ "import pkg_resources\n"
+ "pkg_resources.run_script(%(spec)r, %(script_name)r)\n"
+ ) % locals()
+
+ self.write_script(script_name, script_text)
+
+ def write_script(self, script_name, contents, mode="t", blockers=()):
+ """Write an executable file to the scripts directory"""
+ self.delete_blockers( # clean up old .py/.pyw w/o a script
+ [os.path.join(self.script_dir,x) for x in blockers])
+ log.info("Installing %s script to %s", script_name, self.script_dir)
+ target = os.path.join(self.script_dir, script_name)
+ self.add_output(target)
+
+ if not self.dry_run:
+ ensure_directory(target)
+ f = open(target,"w"+mode)
+ f.write(contents)
+ f.close()
+ try:
+ os.chmod(target,0755)
+ except (AttributeError, os.error):
+ pass
+
+ def install_eggs(self, spec, dist_filename, tmpdir):
+ # .egg dirs or files are already built, so just return them
+ if dist_filename.lower().endswith('.egg'):
+ return [self.install_egg(dist_filename, tmpdir)]
+ elif dist_filename.lower().endswith('.exe'):
+ return [self.install_exe(dist_filename, tmpdir)]
+
+ # Anything else, try to extract and build
+ setup_base = tmpdir
+ if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'):
+ unpack_archive(dist_filename, tmpdir, self.unpack_progress)
+ elif os.path.isdir(dist_filename):
+ setup_base = os.path.abspath(dist_filename)
+
+ if (setup_base.startswith(tmpdir) # something we downloaded
+ and self.build_directory and spec is not None
+ ):
+ setup_base = self.maybe_move(spec, dist_filename, setup_base)
+
+ # Find the setup.py file
+ setup_script = os.path.join(setup_base, 'setup.py')
+
+ if not os.path.exists(setup_script):
+ setups = glob(os.path.join(setup_base, '*', 'setup.py'))
+ if not setups:
+ raise DistutilsError(
+ "Couldn't find a setup script in %s" % dist_filename
+ )
+ if len(setups)>1:
+ raise DistutilsError(
+ "Multiple setup scripts in %s" % dist_filename
+ )
+ setup_script = setups[0]
+
+ # Now run it, and return the result
+ if self.editable:
+ log.warn(self.report_editable(spec, setup_script))
+ return []
+ else:
+ return self.build_and_install(setup_script, setup_base)
+
+ def egg_distribution(self, egg_path):
+ if os.path.isdir(egg_path):
+ metadata = PathMetadata(egg_path,os.path.join(egg_path,'EGG-INFO'))
+ else:
+ metadata = EggMetadata(zipimport.zipimporter(egg_path))
+ return Distribution.from_filename(egg_path,metadata=metadata)
+
+ def install_egg(self, egg_path, tmpdir):
+ destination = os.path.join(self.install_dir,os.path.basename(egg_path))
+ destination = os.path.abspath(destination)
+ if not self.dry_run:
+ ensure_directory(destination)
+
+ dist = self.egg_distribution(egg_path)
+ self.check_conflicts(dist)
+ if not samefile(egg_path, destination):
+ if os.path.isdir(destination) and not os.path.islink(destination):
+ dir_util.remove_tree(destination, dry_run=self.dry_run)
+ elif os.path.exists(destination):
+ self.execute(os.unlink,(destination,),"Removing "+destination)
+ uncache_zipdir(destination)
+ if os.path.isdir(egg_path):
+ if egg_path.startswith(tmpdir):
+ f,m = shutil.move, "Moving"
+ else:
+ f,m = shutil.copytree, "Copying"
+ elif self.should_unzip(dist):
+ self.mkpath(destination)
+ f,m = self.unpack_and_compile, "Extracting"
+ elif egg_path.startswith(tmpdir):
+ f,m = shutil.move, "Moving"
+ else:
+ f,m = shutil.copy2, "Copying"
+
+ self.execute(f, (egg_path, destination),
+ (m+" %s to %s") %
+ (os.path.basename(egg_path),os.path.dirname(destination)))
+
+ self.add_output(destination)
+ return self.egg_distribution(destination)
+
+ def install_exe(self, dist_filename, tmpdir):
+ # See if it's valid, get data
+ cfg = extract_wininst_cfg(dist_filename)
+ if cfg is None:
+ raise DistutilsError(
+ "%s is not a valid distutils Windows .exe" % dist_filename
+ )
+ # Create a dummy distribution object until we build the real distro
+ dist = Distribution(None,
+ project_name=cfg.get('metadata','name'),
+ version=cfg.get('metadata','version'), platform="win32"
+ )
+
+ # Convert the .exe to an unpacked egg
+ egg_path = dist.location = os.path.join(tmpdir, dist.egg_name()+'.egg')
+ egg_tmp = egg_path+'.tmp'
+ egg_info = os.path.join(egg_tmp, 'EGG-INFO')
+ pkg_inf = os.path.join(egg_info, 'PKG-INFO')
+ ensure_directory(pkg_inf) # make sure EGG-INFO dir exists
+ dist._provider = PathMetadata(egg_tmp, egg_info) # XXX
+ self.exe_to_egg(dist_filename, egg_tmp)
+
+ # Write EGG-INFO/PKG-INFO
+ if not os.path.exists(pkg_inf):
+ f = open(pkg_inf,'w')
+ f.write('Metadata-Version: 1.0\n')
+ for k,v in cfg.items('metadata'):
+ if k<>'target_version':
+ f.write('%s: %s\n' % (k.replace('_','-').title(), v))
+ f.close()
+ script_dir = os.path.join(egg_info,'scripts')
+ self.delete_blockers( # delete entry-point scripts to avoid duping
+ [os.path.join(script_dir,args[0]) for args in get_script_args(dist)]
+ )
+ # Build .egg file from tmpdir
+ bdist_egg.make_zipfile(
+ egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run
+ )
+ # install the .egg
+ return self.install_egg(egg_path, tmpdir)
+
+ def exe_to_egg(self, dist_filename, egg_tmp):
+ """Extract a bdist_wininst to the directories an egg would use"""
+ # Check for .pth file and set up prefix translations
+ prefixes = get_exe_prefixes(dist_filename)
+ to_compile = []
+ native_libs = []
+ top_level = {}
+
+ def process(src,dst):
+ for old,new in prefixes:
+ if src.startswith(old):
+ src = new+src[len(old):]
+ parts = src.split('/')
+ dst = os.path.join(egg_tmp, *parts)
+ dl = dst.lower()
+ if dl.endswith('.pyd') or dl.endswith('.dll'):
+ top_level[os.path.splitext(parts[0])[0]] = 1
+ native_libs.append(src)
+ elif dl.endswith('.py') and old!='SCRIPTS/':
+ top_level[os.path.splitext(parts[0])[0]] = 1
+ to_compile.append(dst)
+ return dst
+ if not src.endswith('.pth'):
+ log.warn("WARNING: can't process %s", src)
+ return None
+
+ # extract, tracking .pyd/.dll->native_libs and .py -> to_compile
+ unpack_archive(dist_filename, egg_tmp, process)
+ stubs = []
+ for res in native_libs:
+ if res.lower().endswith('.pyd'): # create stubs for .pyd's
+ parts = res.split('/')
+ resource, parts[-1] = parts[-1], parts[-1][:-1]
+ pyfile = os.path.join(egg_tmp, *parts)
+ to_compile.append(pyfile); stubs.append(pyfile)
+ bdist_egg.write_stub(resource, pyfile)
+
+ self.byte_compile(to_compile) # compile .py's
+ bdist_egg.write_safety_flag(os.path.join(egg_tmp,'EGG-INFO'),
+ bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag
+
+ for name in 'top_level','native_libs':
+ if locals()[name]:
+ txt = os.path.join(egg_tmp, 'EGG-INFO', name+'.txt')
+ if not os.path.exists(txt):
+ open(txt,'w').write('\n'.join(locals()[name])+'\n')
+
+ def check_conflicts(self, dist):
+ """Verify that there are no conflicting "old-style" packages"""
+
+ return dist # XXX temporarily disable until new strategy is stable
+ from imp import find_module, get_suffixes
+ from glob import glob
+
+ blockers = []
+ names = dict.fromkeys(dist._get_metadata('top_level.txt')) # XXX private attr
+
+ exts = {'.pyc':1, '.pyo':1} # get_suffixes() might leave one out
+ for ext,mode,typ in get_suffixes():
+ exts[ext] = 1
+
+ for path,files in expand_paths([self.install_dir]+self.all_site_dirs):
+ for filename in files:
+ base,ext = os.path.splitext(filename)
+ if base in names:
+ if not ext:
+ # no extension, check for package
+ try:
+ f, filename, descr = find_module(base, [path])
+ except ImportError:
+ continue
+ else:
+ if f: f.close()
+ if filename not in blockers:
+ blockers.append(filename)
+ elif ext in exts and base!='site': # XXX ugh
+ blockers.append(os.path.join(path,filename))
+ if blockers:
+ self.found_conflicts(dist, blockers)
+
+ return dist
+
+ def found_conflicts(self, dist, blockers):
+ if self.delete_conflicting:
+ log.warn("Attempting to delete conflicting packages:")
+ return self.delete_blockers(blockers)
+
+ msg = """\
+-------------------------------------------------------------------------
+CONFLICT WARNING:
+
+The following modules or packages have the same names as modules or
+packages being installed, and will be *before* the installed packages in
+Python's search path. You MUST remove all of the relevant files and
+directories before you will be able to use the package(s) you are
+installing:
+
+ %s
+
+""" % '\n '.join(blockers)
+
+ if self.ignore_conflicts_at_my_risk:
+ msg += """\
+(Note: you can run EasyInstall on '%s' with the
+--delete-conflicting option to attempt deletion of the above files
+and/or directories.)
+""" % dist.project_name
+ else:
+ msg += """\
+Note: you can attempt this installation again with EasyInstall, and use
+either the --delete-conflicting (-D) option or the
+--ignore-conflicts-at-my-risk option, to either delete the above files
+and directories, or to ignore the conflicts, respectively. Note that if
+you ignore the conflicts, the installed package(s) may not work.
+"""
+ msg += """\
+-------------------------------------------------------------------------
+"""
+ sys.stderr.write(msg)
+ sys.stderr.flush()
+ if not self.ignore_conflicts_at_my_risk:
+ raise DistutilsError("Installation aborted due to conflicts")
+
+ def installation_report(self, req, dist, what="Installed"):
+ """Helpful installation message for display to package users"""
+ msg = "\n%(what)s %(eggloc)s%(extras)s"
+ if self.multi_version and not self.no_report:
+ msg += """
+
+Because this distribution was installed --multi-version or --install-dir,
+before you can import modules from this package in an application, you
+will need to 'import pkg_resources' and then use a 'require()' call
+similar to one of these examples, in order to select the desired version:
+
+ pkg_resources.require("%(name)s") # latest installed version
+ pkg_resources.require("%(name)s==%(version)s") # this exact version
+ pkg_resources.require("%(name)s>=%(version)s") # this version or higher
+"""
+ if self.install_dir not in map(normalize_path,sys.path):
+ msg += """
+
+Note also that the installation directory must be on sys.path at runtime for
+this to work. (e.g. by being the application's script directory, by being on
+PYTHONPATH, or by being added to sys.path by your code.)
+"""
+ eggloc = dist.location
+ name = dist.project_name
+ version = dist.version
+ extras = '' # TODO: self.report_extras(req, dist)
+ return msg % locals()
+
+ def report_editable(self, spec, setup_script):
+ dirname = os.path.dirname(setup_script)
+ python = sys.executable
+ return """\nExtracted editable version of %(spec)s to %(dirname)s
+
+If it uses setuptools in its setup script, you can activate it in
+"development" mode by going to that directory and running::
+
+ %(python)s setup.py develop
+
+See the setuptools documentation for the "develop" command for more info.
+""" % locals()
+
+ def run_setup(self, setup_script, setup_base, args):
+ sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg)
+ sys.modules.setdefault('distutils.command.egg_info', egg_info)
+
+ args = list(args)
+ if self.verbose>2:
+ v = 'v' * (self.verbose - 1)
+ args.insert(0,'-'+v)
+ elif self.verbose<2:
+ args.insert(0,'-q')
+ if self.dry_run:
+ args.insert(0,'-n')
+ log.info(
+ "Running %s %s", setup_script[len(setup_base)+1:], ' '.join(args)
+ )
+ try:
+ run_setup(setup_script, args)
+ except SystemExit, v:
+ raise DistutilsError("Setup script exited with %s" % (v.args[0],))
+
+ def build_and_install(self, setup_script, setup_base):
+ args = ['bdist_egg', '--dist-dir']
+ dist_dir = tempfile.mkdtemp(
+ prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script)
+ )
+ try:
+ args.append(dist_dir)
+ self.run_setup(setup_script, setup_base, args)
+ all_eggs = Environment([dist_dir])
+ eggs = []
+ for key in all_eggs:
+ for dist in all_eggs[key]:
+ eggs.append(self.install_egg(dist.location, setup_base))
+ if not eggs and not self.dry_run:
+ log.warn("No eggs found in %s (setup script problem?)",
+ dist_dir)
+ return eggs
+ finally:
+ rmtree(dist_dir)
+ log.set_verbosity(self.verbose) # restore our log verbosity
+
+ def update_pth(self,dist):
+ if self.pth_file is None:
+ return
+
+ for d in self.pth_file[dist.key]: # drop old entries
+ if self.multi_version or d.location != dist.location:
+ log.info("Removing %s from easy-install.pth file", d)
+ self.pth_file.remove(d)
+ if d.location in self.shadow_path:
+ self.shadow_path.remove(d.location)
+
+ if not self.multi_version:
+ if dist.location in self.pth_file.paths:
+ log.info(
+ "%s is already the active version in easy-install.pth",
+ dist
+ )
+ else:
+ log.info("Adding %s to easy-install.pth file", dist)
+ self.pth_file.add(dist) # add new entry
+ if dist.location not in self.shadow_path:
+ self.shadow_path.append(dist.location)
+
+ if not self.dry_run:
+
+ self.pth_file.save()
+
+ if dist.key=='setuptools':
+ # Ensure that setuptools itself never becomes unavailable!
+ # XXX should this check for latest version?
+ filename = os.path.join(self.install_dir,'setuptools.pth')
+ if os.path.islink(filename): os.unlink(filename)
+ f = open(filename, 'wt')
+ f.write(self.pth_file.make_relative(dist.location)+'\n')
+ f.close()
+
+ def unpack_progress(self, src, dst):
+ # Progress filter for unpacking
+ log.debug("Unpacking %s to %s", src, dst)
+ return dst # only unpack-and-compile skips files for dry run
+
+ def unpack_and_compile(self, egg_path, destination):
+ to_compile = []
+
+ def pf(src,dst):
+ if dst.endswith('.py') and not src.startswith('EGG-INFO/'):
+ to_compile.append(dst)
+ self.unpack_progress(src,dst)
+ return not self.dry_run and dst or None
+
+ unpack_archive(egg_path, destination, pf)
+ self.byte_compile(to_compile)
+
+
+ def byte_compile(self, to_compile):
+ from distutils.util import byte_compile
+ try:
+ # try to make the byte compile messages quieter
+ log.set_verbosity(self.verbose - 1)
+
+ byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run)
+ if self.optimize:
+ byte_compile(
+ to_compile, optimize=self.optimize, force=1,
+ dry_run=self.dry_run
+ )
+ finally:
+ log.set_verbosity(self.verbose) # restore original verbosity
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ def no_default_version_msg(self):
+ return """bad install directory or PYTHONPATH
+
+You are attempting to install a package to a directory that is not
+on PYTHONPATH and which Python does not read ".pth" files from. The
+installation directory you specified (via --install-dir, --prefix, or
+the distutils default setting) was:
+
+ %s
+
+and your PYTHONPATH environment variable currently contains:
+
+ %r
+
+Here are some of your options for correcting the problem:
+
+* You can choose a different installation directory, i.e., one that is
+ on PYTHONPATH or supports .pth files
+
+* You can add the installation directory to the PYTHONPATH environment
+ variable. (It must then also be on PYTHONPATH whenever you run
+ Python and want to use the package(s) you are installing.)
+
+* You can set up the installation directory to support ".pth" files by
+ using one of the approaches described here:
+
+ http://peak.telecommunity.com/EasyInstall.html#custom-installation-locations
+
+Please make the appropriate changes for your system and try again.""" % (
+ self.install_dir, os.environ.get('PYTHONPATH','')
+ )
+
+
+
+
+
+
+
+
+
+
+ def install_site_py(self):
+ """Make sure there's a site.py in the target dir, if needed"""
+
+ if self.sitepy_installed:
+ return # already did it, or don't need to
+
+ sitepy = os.path.join(self.install_dir, "site.py")
+ source = resource_string("setuptools", "site-patch.py")
+ current = ""
+
+ if os.path.exists(sitepy):
+ log.debug("Checking existing site.py in %s", self.install_dir)
+ current = open(sitepy,'rb').read()
+ if not current.startswith('def __boot():'):
+ raise DistutilsError(
+ "%s is not a setuptools-generated site.py; please"
+ " remove it." % sitepy
+ )
+
+ if current != source:
+ log.info("Creating %s", sitepy)
+ if not self.dry_run:
+ ensure_directory(sitepy)
+ f = open(sitepy,'wb')
+ f.write(source)
+ f.close()
+ self.byte_compile([sitepy])
+
+ self.sitepy_installed = True
+
+
+
+
+
+
+
+
+
+
+
+
+ INSTALL_SCHEMES = dict(
+ posix = dict(
+ install_dir = '$base/lib/python$py_version_short/site-packages',
+ script_dir = '$base/bin',
+ ),
+ )
+
+ DEFAULT_SCHEME = dict(
+ install_dir = '$base/Lib/site-packages',
+ script_dir = '$base/Scripts',
+ )
+
+ def _expand(self, *attrs):
+ config_vars = self.get_finalized_command('install').config_vars
+
+ if self.prefix:
+ # Set default install_dir/scripts from --prefix
+ config_vars = config_vars.copy()
+ config_vars['base'] = self.prefix
+ scheme = self.INSTALL_SCHEMES.get(os.name,self.DEFAULT_SCHEME)
+ for attr,val in scheme.items():
+ if getattr(self,attr,None) is None:
+ setattr(self,attr,val)
+
+ from distutils.util import subst_vars
+ for attr in attrs:
+ val = getattr(self, attr)
+ if val is not None:
+ val = subst_vars(val, config_vars)
+ if os.name == 'posix':
+ val = os.path.expanduser(val)
+ setattr(self, attr, val)
+
+
+
+
+
+
+
+
+
+def get_site_dirs():
+ # return a list of 'site' dirs
+ sitedirs = filter(None,os.environ.get('PYTHONPATH','').split(os.pathsep))
+ 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.append(os.path.join(prefix, "Lib", "site-packages"))
+ elif os.sep == '/':
+ sitedirs.extend([os.path.join(prefix,
+ "lib",
+ "python" + sys.version[:3],
+ "site-packages"),
+ os.path.join(prefix, "lib", "site-python")])
+ else:
+ sitedirs.extend(
+ [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 plat_specific in (0,1):
+ site_lib = get_python_lib(plat_specific)
+ if site_lib not in sitedirs: sitedirs.append(site_lib)
+
+ sitedirs = map(normalize_path, sitedirs)
+ return sitedirs
+
+
+def expand_paths(inputs):
+ """Yield sys.path directories that might contain "old-style" packages"""
+
+ seen = {}
+
+ for dirname in inputs:
+ dirname = normalize_path(dirname)
+ if dirname in seen:
+ continue
+
+ seen[dirname] = 1
+ if not os.path.isdir(dirname):
+ continue
+
+ files = os.listdir(dirname)
+ yield dirname, files
+
+ for name in files:
+ if not name.endswith('.pth'):
+ # We only care about the .pth files
+ continue
+ if name in ('easy-install.pth','setuptools.pth'):
+ # Ignore .pth files that we control
+ continue
+
+ # Read the .pth file
+ f = open(os.path.join(dirname,name))
+ lines = list(yield_lines(f))
+ f.close()
+
+ # Yield existing non-dupe, non-import directory lines from it
+ for line in lines:
+ if not line.startswith("import"):
+ line = normalize_path(line.rstrip())
+ if line not in seen:
+ seen[line] = 1
+ if not os.path.isdir(line):
+ continue
+ yield line, os.listdir(line)
+
+
+def extract_wininst_cfg(dist_filename):
+ """Extract configuration data from a bdist_wininst .exe
+
+ Returns a ConfigParser.RawConfigParser, or None
+ """
+ f = open(dist_filename,'rb')
+ try:
+ endrec = zipfile._EndRecData(f)
+ if endrec is None:
+ return None
+
+ prepended = (endrec[9] - endrec[5]) - endrec[6]
+ if prepended < 12: # no wininst data here
+ return None
+ f.seek(prepended-12)
+
+ import struct, StringIO, ConfigParser
+ tag, cfglen, bmlen = struct.unpack("<iii",f.read(12))
+ if tag not in (0x1234567A, 0x1234567B):
+ return None # not a valid tag
+
+ f.seek(prepended-(12+cfglen+bmlen))
+ cfg = ConfigParser.RawConfigParser({'version':'','target_version':''})
+ try:
+ cfg.readfp(StringIO.StringIO(f.read(cfglen).split(chr(0),1)[0]))
+ except ConfigParser.Error:
+ return None
+ if not cfg.has_section('metadata') or not cfg.has_section('Setup'):
+ return None
+ return cfg
+
+ finally:
+ f.close()
+
+
+
+
+
+
+
+
+def get_exe_prefixes(exe_filename):
+ """Get exe->egg path translations for a given .exe file"""
+
+ prefixes = [
+ ('PURELIB/', ''),
+ ('PLATLIB/', ''),
+ ('SCRIPTS/', 'EGG-INFO/scripts/')
+ ]
+ z = zipfile.ZipFile(exe_filename)
+ try:
+ for info in z.infolist():
+ name = info.filename
+ parts = name.split('/')
+ if len(parts)==3 and parts[2]=='PKG-INFO':
+ if parts[1].endswith('.egg-info'):
+ prefixes.insert(0,('/'.join(parts[:2]), 'EGG-INFO/'))
+ break
+ if len(parts)<>2 or not name.endswith('.pth'):
+ continue
+ if name.endswith('-nspkg.pth'):
+ continue
+ if parts[0] in ('PURELIB','PLATLIB'):
+ for pth in yield_lines(z.read(name)):
+ pth = pth.strip().replace('\\','/')
+ if not pth.startswith('import'):
+ prefixes.append((('%s/%s/' % (parts[0],pth)), ''))
+ finally:
+ z.close()
+
+ prefixes.sort(); prefixes.reverse()
+ return prefixes
+
+
+def parse_requirement_arg(spec):
+ try:
+ return Requirement.parse(spec)
+ except ValueError:
+ raise DistutilsError(
+ "Not a URL, existing file, or requirement spec: %r" % (spec,)
+ )
+
+class PthDistributions(Environment):
+ """A .pth file with Distribution paths in it"""
+
+ dirty = False
+
+ def __init__(self, filename):
+ self.filename = filename
+ self.basedir = normalize_path(os.path.dirname(self.filename))
+ self._load(); Environment.__init__(self, [], None, None)
+ for path in yield_lines(self.paths):
+ map(self.add, find_distributions(path, True))
+
+ def _load(self):
+ self.paths = []
+ saw_import = False
+ seen = {}
+ if os.path.isfile(self.filename):
+ for line in open(self.filename,'rt'):
+ if line.startswith('import'):
+ saw_import = True
+ continue
+ path = line.rstrip()
+ self.paths.append(path)
+ if not path.strip() or path.strip().startswith('#'):
+ continue
+ # skip non-existent paths, in case somebody deleted a package
+ # manually, and duplicate paths as well
+ path = self.paths[-1] = normalize_path(
+ os.path.join(self.basedir,path)
+ )
+ if not os.path.exists(path) or path in seen:
+ self.paths.pop() # skip it
+ self.dirty = True # we cleaned up, so we're dirty now :)
+ continue
+ seen[path] = 1
+
+ if self.paths and not saw_import:
+ self.dirty = True # ensure anything we touch has import wrappers
+ while self.paths and not self.paths[-1].strip():
+ self.paths.pop()
+
+ def save(self):
+ """Write changed .pth file back to disk"""
+ if not self.dirty:
+ return
+
+ data = '\n'.join(map(self.make_relative,self.paths))
+ if data:
+ log.debug("Saving %s", self.filename)
+ data = (
+ "import sys; sys.__plen = len(sys.path)\n"
+ "%s\n"
+ "import sys; new=sys.path[sys.__plen:];"
+ " del sys.path[sys.__plen:];"
+ " p=getattr(sys,'__egginsert',0); sys.path[p:p]=new;"
+ " sys.__egginsert = p+len(new)\n"
+ ) % data
+
+ if os.path.islink(self.filename):
+ os.unlink(self.filename)
+ f = open(self.filename,'wb')
+ f.write(data); f.close()
+
+ elif os.path.exists(self.filename):
+ log.debug("Deleting empty %s", self.filename)
+ os.unlink(self.filename)
+
+ self.dirty = False
+
+ def add(self,dist):
+ """Add `dist` to the distribution map"""
+ if dist.location not in self.paths:
+ self.paths.append(dist.location); self.dirty = True
+ Environment.add(self,dist)
+
+ def remove(self,dist):
+ """Remove `dist` from the distribution map"""
+ while dist.location in self.paths:
+ self.paths.remove(dist.location); self.dirty = True
+ Environment.remove(self,dist)
+
+
+ def make_relative(self,path):
+ if normalize_path(os.path.dirname(path))==self.basedir:
+ return os.path.basename(path)
+ return path
+
+
+def get_script_header(script_text, executable=sys_executable):
+ """Create a #! line, getting options (if any) from script_text"""
+ from distutils.command.build_scripts import first_line_re
+ first, rest = (script_text+'\n').split('\n',1)
+ match = first_line_re.match(first)
+ options = ''
+ if match:
+ script_text = rest
+ options = match.group(1) or ''
+ if options:
+ options = ' '+options
+ return "#!%(executable)s%(options)s\n" % locals()
+
+
+def auto_chmod(func, arg, exc):
+ if func is os.remove and os.name=='nt':
+ os.chmod(arg, stat.S_IWRITE)
+ return func(arg)
+ exc = sys.exc_info()
+ raise exc[0], (exc[1][0], exc[1][1] + (" %s %s" % (func,arg)))
+
+
+def uncache_zipdir(path):
+ """Ensure that the zip directory cache doesn't have stale info for path"""
+ from zipimport import _zip_directory_cache as zdc
+ if path in zdc:
+ del zdc[path]
+ else:
+ path = normalize_path(path)
+ for p in zdc:
+ if normalize_path(p)==path:
+ del zdc[p]
+ return
+
+
+def get_script_args(dist, executable=sys_executable):
+ """Yield write_script() argument tuples for a distribution's entrypoints"""
+ spec = str(dist.as_requirement())
+ header = get_script_header("", executable)
+ for group in 'console_scripts', 'gui_scripts':
+ for name,ep in dist.get_entry_map(group).items():
+ script_text = (
+ "# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n"
+ "__requires__ = %(spec)r\n"
+ "import sys\n"
+ "from pkg_resources import load_entry_point\n"
+ "\n"
+ "sys.exit(\n"
+ " load_entry_point(%(spec)r, %(group)r, %(name)r)()\n"
+ ")\n"
+ ) % locals()
+ if sys.platform=='win32':
+ # On Windows, add a .py extension and an .exe launcher
+ if group=='gui_scripts':
+ ext, launcher = '-script.pyw', 'gui.exe'
+ old = ['.pyw']
+ new_header = re.sub('(?i)python.exe','pythonw.exe',header)
+ else:
+ ext, launcher = '-script.py', 'cli.exe'
+ old = ['.py','.pyc','.pyo']
+ new_header = re.sub('(?i)pythonw.exe','pythonw.exe',header)
+
+ if os.path.exists(new_header[2:-1]):
+ hdr = new_header
+ else:
+ hdr = header
+ yield (name+ext, hdr+script_text, 't', [name+x for x in old])
+ yield (
+ name+'.exe', resource_string('setuptools', launcher),
+ 'b' # write in binary mode
+ )
+ else:
+ # On other platforms, we assume the right thing to do is to
+ # just write the stub with no extension.
+ yield (name, header+script_text)
+
+def rmtree(path, ignore_errors=False, onerror=auto_chmod):
+ """Recursively delete a directory tree.
+
+ This code is taken from the Python 2.4 version of 'shutil', because
+ the 2.3 version doesn't really work right.
+ """
+ if ignore_errors:
+ def onerror(*args):
+ pass
+ elif onerror is None:
+ def onerror(*args):
+ raise
+ names = []
+ try:
+ names = os.listdir(path)
+ except os.error, err:
+ onerror(os.listdir, path, sys.exc_info())
+ for name in names:
+ fullname = os.path.join(path, name)
+ try:
+ mode = os.lstat(fullname).st_mode
+ except os.error:
+ mode = 0
+ if stat.S_ISDIR(mode):
+ rmtree(fullname, ignore_errors, onerror)
+ else:
+ try:
+ os.remove(fullname)
+ except os.error, err:
+ onerror(os.remove, fullname, sys.exc_info())
+ try:
+ os.rmdir(path)
+ except os.error:
+ onerror(os.rmdir, path, sys.exc_info())
+
+
+
+
+
+
+
+def main(argv=None, **kw):
+ from setuptools import setup
+ from setuptools.dist import Distribution
+ import distutils.core
+
+ USAGE = """\
+usage: %(script)s [options] requirement_or_url ...
+ or: %(script)s --help
+"""
+
+ def gen_usage (script_name):
+ script = os.path.basename(script_name)
+ return USAGE % vars()
+
+ def with_ei_usage(f):
+ old_gen_usage = distutils.core.gen_usage
+ try:
+ distutils.core.gen_usage = gen_usage
+ return f()
+ finally:
+ distutils.core.gen_usage = old_gen_usage
+
+ class DistributionWithoutHelpCommands(Distribution):
+ def _show_help(self,*args,**kw):
+ with_ei_usage(lambda: Distribution._show_help(self,*args,**kw))
+
+ if argv is None:
+ argv = sys.argv[1:]
+
+ with_ei_usage(lambda:
+ setup(
+ script_args = ['-q','easy_install', '-v']+argv,
+ script_name = sys.argv[0] or 'easy_install',
+ distclass=DistributionWithoutHelpCommands, **kw
+ )
+ )
diff --git a/Lib/setuptools/command/egg_info.py b/Lib/setuptools/command/egg_info.py
new file mode 100755
index 0000000..b68fb39
--- /dev/null
+++ b/Lib/setuptools/command/egg_info.py
@@ -0,0 +1,365 @@
+"""setuptools.command.egg_info
+
+Create a distribution's .egg-info directory and contents"""
+
+# This module should be kept compatible with Python 2.3
+import os, re
+from setuptools import Command
+from distutils.errors import *
+from distutils import log
+from setuptools.command.sdist import sdist
+from distutils import file_util
+from distutils.util import convert_path
+from distutils.filelist import FileList
+from pkg_resources import parse_requirements, safe_name, parse_version, \
+ safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename
+from sdist import walk_revctrl
+
+class egg_info(Command):
+ description = "create a distribution's .egg-info directory"
+
+ user_options = [
+ ('egg-base=', 'e', "directory containing .egg-info directories"
+ " (default: top of the source tree)"),
+ ('tag-svn-revision', 'r',
+ "Add subversion revision ID to version number"),
+ ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
+ ('tag-build=', 'b', "Specify explicit tag to add to version number"),
+ ]
+
+ boolean_options = ['tag-date','tag-svn-revision']
+
+ def initialize_options (self):
+ self.egg_name = None
+ self.egg_version = None
+ self.egg_base = None
+ self.egg_info = None
+ self.tag_build = None
+ self.tag_svn_revision = 0
+ self.tag_date = 0
+ self.broken_egg_info = False
+
+ def finalize_options (self):
+ self.egg_name = safe_name(self.distribution.get_name())
+ self.egg_version = self.tagged_version()
+
+ try:
+ list(
+ parse_requirements('%s==%s' % (self.egg_name,self.egg_version))
+ )
+ except ValueError:
+ raise DistutilsOptionError(
+ "Invalid distribution name or version syntax: %s-%s" %
+ (self.egg_name,self.egg_version)
+ )
+
+ if self.egg_base is None:
+ dirs = self.distribution.package_dir
+ self.egg_base = (dirs or {}).get('',os.curdir)
+
+ self.ensure_dirname('egg_base')
+ self.egg_info = to_filename(self.egg_name)+'.egg-info'
+ if self.egg_base != os.curdir:
+ self.egg_info = os.path.join(self.egg_base, self.egg_info)
+ if '-' in self.egg_name: self.check_broken_egg_info()
+
+ # Set package version for the benefit of dumber commands
+ # (e.g. sdist, bdist_wininst, etc.)
+ #
+ self.distribution.metadata.version = self.egg_version
+
+ # If we bootstrapped around the lack of a PKG-INFO, as might be the
+ # case in a fresh checkout, make sure that any special tags get added
+ # to the version info
+ #
+ pd = self.distribution._patched_dist
+ if pd is not None and pd.key==self.egg_name.lower():
+ pd._version = self.egg_version
+ pd._parsed_version = parse_version(self.egg_version)
+ self.distribution._patched_dist = None
+
+
+
+ def write_or_delete_file(self, what, filename, data, force=False):
+ """Write `data` to `filename` or delete if empty
+
+ If `data` is non-empty, this routine is the same as ``write_file()``.
+ If `data` is empty but not ``None``, this is the same as calling
+ ``delete_file(filename)`. If `data` is ``None``, then this is a no-op
+ unless `filename` exists, in which case a warning is issued about the
+ orphaned file (if `force` is false), or deleted (if `force` is true).
+ """
+ if data:
+ self.write_file(what, filename, data)
+ elif os.path.exists(filename):
+ if data is None and not force:
+ log.warn(
+ "%s not set in setup(), but %s exists", what, filename
+ )
+ return
+ else:
+ self.delete_file(filename)
+
+ def write_file(self, what, filename, data):
+ """Write `data` to `filename` (if not a dry run) after announcing it
+
+ `what` is used in a log message to identify what is being written
+ to the file.
+ """
+ log.info("writing %s to %s", what, filename)
+ if not self.dry_run:
+ f = open(filename, 'wb')
+ f.write(data)
+ f.close()
+
+ def delete_file(self, filename):
+ """Delete `filename` (if not a dry run) after announcing it"""
+ log.info("deleting %s", filename)
+ if not self.dry_run:
+ os.unlink(filename)
+
+
+
+
+ def run(self):
+ self.mkpath(self.egg_info)
+ installer = self.distribution.fetch_build_egg
+ for ep in iter_entry_points('egg_info.writers'):
+ writer = ep.load(installer=installer)
+ writer(self, ep.name, os.path.join(self.egg_info,ep.name))
+ self.find_sources()
+
+ def tagged_version(self):
+ version = self.distribution.get_version()
+ if self.tag_build:
+ version+=self.tag_build
+ if self.tag_svn_revision and (
+ os.path.exists('.svn') or os.path.exists('PKG-INFO')
+ ): version += '-r%s' % self.get_svn_revision()
+ if self.tag_date:
+ import time; version += time.strftime("-%Y%m%d")
+ return safe_version(version)
+
+ def get_svn_revision(self):
+ revision = 0
+ urlre = re.compile('url="([^"]+)"')
+ revre = re.compile('committed-rev="(\d+)"')
+ for base,dirs,files in os.walk(os.curdir):
+ if '.svn' not in dirs:
+ dirs[:] = []
+ continue # no sense walking uncontrolled subdirs
+ dirs.remove('.svn')
+ f = open(os.path.join(base,'.svn','entries'))
+ data = f.read()
+ f.close()
+ dirurl = urlre.search(data).group(1) # get repository URL
+ if base==os.curdir:
+ base_url = dirurl+'/' # save the root url
+ elif not dirurl.startswith(base_url):
+ dirs[:] = []
+ continue # not part of the same svn tree, skip it
+ for match in revre.finditer(data):
+ revision = max(revision, int(match.group(1)))
+ return str(revision or get_pkg_info_revision())
+
+ def find_sources(self):
+ """Generate SOURCES.txt manifest file"""
+ manifest_filename = os.path.join(self.egg_info,"SOURCES.txt")
+ mm = manifest_maker(self.distribution)
+ mm.manifest = manifest_filename
+ mm.run()
+ self.filelist = mm.filelist
+
+ def check_broken_egg_info(self):
+ bei = self.egg_name+'.egg-info'
+ if self.egg_base != os.curdir:
+ bei = os.path.join(self.egg_base, bei)
+ if os.path.exists(bei):
+ log.warn(
+ "-"*78+'\n'
+ "Note: Your current .egg-info directory has a '-' in its name;"
+ '\nthis will not work correctly with "setup.py develop".\n\n'
+ 'Please rename %s to %s to correct this problem.\n'+'-'*78,
+ bei, self.egg_info
+ )
+ self.broken_egg_info = self.egg_info
+ self.egg_info = bei # make it work for now
+
+class FileList(FileList):
+ """File list that accepts only existing, platform-independent paths"""
+
+ def append(self, item):
+ path = convert_path(item)
+ if os.path.exists(path):
+ self.files.append(path)
+
+
+
+
+
+
+
+
+
+
+
+class manifest_maker(sdist):
+
+ template = "MANIFEST.in"
+
+ def initialize_options (self):
+ self.use_defaults = 1
+ self.prune = 1
+ self.manifest_only = 1
+ self.force_manifest = 1
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ self.filelist = FileList()
+ if not os.path.exists(self.manifest):
+ self.write_manifest() # it must exist so it'll get in the list
+ self.filelist.findall()
+ self.add_defaults()
+ if os.path.exists(self.template):
+ self.read_template()
+ self.prune_file_list()
+ self.filelist.sort()
+ self.filelist.remove_duplicates()
+ self.write_manifest()
+
+ def write_manifest (self):
+ """Write the file list in 'self.filelist' (presumably as filled in
+ by 'add_defaults()' and 'read_template()') to the manifest file
+ named by 'self.manifest'.
+ """
+ files = self.filelist.files
+ if os.sep!='/':
+ files = [f.replace(os.sep,'/') for f in files]
+ self.execute(file_util.write_file, (self.manifest, files),
+ "writing manifest file '%s'" % self.manifest)
+
+
+
+
+
+ def add_defaults(self):
+ sdist.add_defaults(self)
+ self.filelist.append(self.template)
+ self.filelist.append(self.manifest)
+ rcfiles = list(walk_revctrl())
+ if rcfiles:
+ self.filelist.extend(rcfiles)
+ elif os.path.exists(self.manifest):
+ self.read_manifest()
+ ei_cmd = self.get_finalized_command('egg_info')
+ self.filelist.include_pattern("*", prefix=ei_cmd.egg_info)
+
+ def prune_file_list (self):
+ build = self.get_finalized_command('build')
+ base_dir = self.distribution.get_fullname()
+ self.filelist.exclude_pattern(None, prefix=build.build_base)
+ self.filelist.exclude_pattern(None, prefix=base_dir)
+ sep = re.escape(os.sep)
+ self.filelist.exclude_pattern(sep+r'(RCS|CVS|\.svn)'+sep, is_regex=1)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def write_pkg_info(cmd, basename, filename):
+ log.info("writing %s", filename)
+ if not cmd.dry_run:
+ metadata = cmd.distribution.metadata
+ metadata.version, oldver = cmd.egg_version, metadata.version
+ metadata.name, oldname = cmd.egg_name, metadata.name
+ try:
+ # write unescaped data to PKG-INFO, so older pkg_resources
+ # can still parse it
+ metadata.write_pkg_info(cmd.egg_info)
+ finally:
+ metadata.name, metadata.version = oldname, oldver
+
+ safe = getattr(cmd.distribution,'zip_safe',None)
+ import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe)
+
+def warn_depends_obsolete(cmd, basename, filename):
+ if os.path.exists(filename):
+ log.warn(
+ "WARNING: 'depends.txt' is not used by setuptools 0.6!\n"
+ "Use the install_requires/extras_require setup() args instead."
+ )
+
+
+def write_requirements(cmd, basename, filename):
+ dist = cmd.distribution
+ data = ['\n'.join(yield_lines(dist.install_requires or ()))]
+ for extra,reqs in (dist.extras_require or {}).items():
+ data.append('\n\n[%s]\n%s' % (extra, '\n'.join(yield_lines(reqs))))
+ cmd.write_or_delete_file("requirements", filename, ''.join(data))
+
+def write_toplevel_names(cmd, basename, filename):
+ pkgs = dict.fromkeys(
+ [k.split('.',1)[0]
+ for k in cmd.distribution.iter_distribution_names()
+ ]
+ )
+ cmd.write_file("top-level names", filename, '\n'.join(pkgs)+'\n')
+
+
+
+def overwrite_arg(cmd, basename, filename):
+ write_arg(cmd, basename, filename, True)
+
+def write_arg(cmd, basename, filename, force=False):
+ argname = os.path.splitext(basename)[0]
+ value = getattr(cmd.distribution, argname, None)
+ if value is not None:
+ value = '\n'.join(value)+'\n'
+ cmd.write_or_delete_file(argname, filename, value, force)
+
+def write_entries(cmd, basename, filename):
+ ep = cmd.distribution.entry_points
+
+ if isinstance(ep,basestring) or ep is None:
+ data = ep
+ elif ep is not None:
+ data = []
+ for section, contents in ep.items():
+ if not isinstance(contents,basestring):
+ contents = EntryPoint.parse_group(section, contents)
+ contents = '\n'.join(map(str,contents.values()))
+ data.append('[%s]\n%s\n\n' % (section,contents))
+ data = ''.join(data)
+
+ cmd.write_or_delete_file('entry points', filename, data, True)
+
+def get_pkg_info_revision():
+ # See if we can get a -r### off of PKG-INFO, in case this is an sdist of
+ # a subversion revision
+ #
+ if os.path.exists('PKG-INFO'):
+ f = open('PKG-INFO','rU')
+ for line in f:
+ match = re.match(r"Version:.*-r(\d+)\s*$", line)
+ if match:
+ return int(match.group(1))
+ return 0
diff --git a/Lib/setuptools/command/install.py b/Lib/setuptools/command/install.py
new file mode 100644
index 0000000..bfb9af5
--- /dev/null
+++ b/Lib/setuptools/command/install.py
@@ -0,0 +1,101 @@
+import setuptools, sys
+from distutils.command.install import install as _install
+from distutils.errors import DistutilsArgError
+
+class install(_install):
+ """Use easy_install to install the package, w/dependencies"""
+
+ user_options = _install.user_options + [
+ ('old-and-unmanageable', None, "Try not to use this!"),
+ ('single-version-externally-managed', None,
+ "used by system package builders to create 'flat' eggs"),
+ ]
+ boolean_options = _install.boolean_options + [
+ 'old-and-unmanageable', 'single-version-externally-managed',
+ ]
+ new_commands = [
+ ('install_egg_info', lambda self: True),
+ ('install_scripts', lambda self: True),
+ ]
+ _nc = dict(new_commands)
+ sub_commands = [
+ cmd for cmd in _install.sub_commands if cmd[0] not in _nc
+ ] + new_commands
+
+ def initialize_options(self):
+ _install.initialize_options(self)
+ self.old_and_unmanageable = None
+ self.single_version_externally_managed = None
+ self.no_compile = None # make DISTUTILS_DEBUG work right!
+
+ def finalize_options(self):
+ _install.finalize_options(self)
+ if self.root:
+ self.single_version_externally_managed = True
+ elif self.single_version_externally_managed:
+ if not self.root and not self.record:
+ raise DistutilsArgError(
+ "You must specify --record or --root when building system"
+ " packages"
+ )
+
+ def handle_extra_path(self):
+ # We always ignore extra_path, because we install as .egg or .egg-info
+ self.path_file = None
+ self.extra_dirs = ''
+
+ def run(self):
+ # Explicit request for old-style install? Just do it
+ if self.old_and_unmanageable or self.single_version_externally_managed:
+ return _install.run(self)
+
+ # Attempt to detect whether we were called from setup() or by another
+ # command. If we were called by setup(), our caller will be the
+ # 'run_command' method in 'distutils.dist', and *its* caller will be
+ # the 'run_commands' method. If we were called any other way, our
+ # immediate caller *might* be 'run_command', but it won't have been
+ # called by 'run_commands'. This is slightly kludgy, but seems to
+ # work.
+ #
+ caller = sys._getframe(2)
+ caller_module = caller.f_globals.get('__name__','')
+ caller_name = caller.f_code.co_name
+
+ if caller_module != 'distutils.dist' or caller_name!='run_commands':
+ # We weren't called from the command line or setup(), so we
+ # should run in backward-compatibility mode to support bdist_*
+ # commands.
+ _install.run(self)
+ else:
+ self.do_egg_install()
+
+
+
+
+
+
+
+
+
+
+
+
+ def do_egg_install(self):
+
+ from setuptools.command.easy_install import easy_install
+
+ cmd = easy_install(
+ self.distribution, args="x", root=self.root, record=self.record,
+ )
+ cmd.ensure_finalized() # finalize before bdist_egg munges install cmd
+
+ self.run_command('bdist_egg')
+ args = [self.distribution.get_command_obj('bdist_egg').egg_output]
+
+ if setuptools.bootstrap_install_from:
+ # Bootstrap self-installation of setuptools
+ args.insert(0, setuptools.bootstrap_install_from)
+
+ cmd.args = args
+ cmd.run()
+ setuptools.bootstrap_install_from = None
diff --git a/Lib/setuptools/command/install_egg_info.py b/Lib/setuptools/command/install_egg_info.py
new file mode 100755
index 0000000..193e91a
--- /dev/null
+++ b/Lib/setuptools/command/install_egg_info.py
@@ -0,0 +1,81 @@
+from setuptools import Command
+from setuptools.archive_util import unpack_archive
+from distutils import log, dir_util
+import os, shutil, pkg_resources
+
+class install_egg_info(Command):
+ """Install an .egg-info directory for the package"""
+
+ description = "Install an .egg-info directory for the package"
+
+ user_options = [
+ ('install-dir=', 'd', "directory to install to"),
+ ]
+
+ def initialize_options(self):
+ self.install_dir = None
+
+ def finalize_options(self):
+ self.set_undefined_options('install_lib',('install_dir','install_dir'))
+ ei_cmd = self.get_finalized_command("egg_info")
+ basename = pkg_resources.Distribution(
+ None, None, ei_cmd.egg_name, ei_cmd.egg_version
+ ).egg_name()+'.egg-info'
+ self.source = ei_cmd.egg_info
+ self.target = os.path.join(self.install_dir, basename)
+ self.outputs = [self.target]
+
+ def run(self):
+ self.run_command('egg_info')
+ target = self.target
+ if os.path.isdir(self.target) and not os.path.islink(self.target):
+ dir_util.remove_tree(self.target, dry_run=self.dry_run)
+ elif os.path.exists(self.target):
+ self.execute(os.unlink,(self.target,),"Removing "+self.target)
+ if not self.dry_run:
+ pkg_resources.ensure_directory(self.target)
+ self.execute(self.copytree, (),
+ "Copying %s to %s" % (self.source, self.target)
+ )
+ self.install_namespaces()
+
+ def get_outputs(self):
+ return self.outputs
+
+ def copytree(self):
+ # Copy the .egg-info tree to site-packages
+ def skimmer(src,dst):
+ # filter out source-control directories; note that 'src' is always
+ # a '/'-separated path, regardless of platform. 'dst' is a
+ # platform-specific path.
+ for skip in '.svn/','CVS/':
+ if src.startswith(skip) or '/'+skip in src:
+ return None
+ self.outputs.append(dst)
+ log.debug("Copying %s to %s", src, dst)
+ return dst
+ unpack_archive(self.source, self.target, skimmer)
+
+ def install_namespaces(self):
+ nsp = (self.distribution.namespace_packages or [])[:]
+ if not nsp: return
+ nsp.sort() # set up shorter names first
+ filename,ext = os.path.splitext(self.target)
+ filename += '-nspkg.pth'; self.outputs.append(filename)
+ log.info("Installing %s",filename)
+ if not self.dry_run:
+ f = open(filename,'wb')
+ for pkg in nsp:
+ pth = tuple(pkg.split('.'))
+ f.write(
+ "import sys,new,os; "
+ "p = os.path.join(sys._getframe(1).f_locals['sitedir'], "
+ "*%(pth)r); "
+ "ie = os.path.exists(os.path.join(p,'__init__.py')); "
+ "m = not ie and "
+ "sys.modules.setdefault(%(pkg)r,new.module(%(pkg)r)); "
+ "mp = (m or []) and m.__dict__.setdefault('__path__',[]); "
+ "(p not in mp) and mp.append(p)\n"
+ % locals()
+ )
+ f.close()
diff --git a/Lib/setuptools/command/install_lib.py b/Lib/setuptools/command/install_lib.py
new file mode 100644
index 0000000..96c8dfe
--- /dev/null
+++ b/Lib/setuptools/command/install_lib.py
@@ -0,0 +1,76 @@
+from distutils.command.install_lib import install_lib as _install_lib
+import os
+
+class install_lib(_install_lib):
+ """Don't add compiled flags to filenames of non-Python files"""
+
+ def _bytecode_filenames (self, py_filenames):
+ bytecode_files = []
+ for py_file in py_filenames:
+ if not py_file.endswith('.py'):
+ continue
+ if self.compile:
+ bytecode_files.append(py_file + "c")
+ if self.optimize > 0:
+ bytecode_files.append(py_file + "o")
+
+ return bytecode_files
+
+ def run(self):
+ self.build()
+ outfiles = self.install()
+ if outfiles is not None:
+ # always compile, in case we have any extension stubs to deal with
+ self.byte_compile(outfiles)
+
+ def get_exclusions(self):
+ exclude = {}
+ nsp = self.distribution.namespace_packages
+
+ if (nsp and self.get_finalized_command('install')
+ .single_version_externally_managed
+ ):
+ for pkg in nsp:
+ parts = pkg.split('.')
+ while parts:
+ pkgdir = os.path.join(self.install_dir, *parts)
+ for f in '__init__.py', '__init__.pyc', '__init__.pyo':
+ exclude[os.path.join(pkgdir,f)] = 1
+ parts.pop()
+ return exclude
+
+ def copy_tree(
+ self, infile, outfile,
+ preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1
+ ):
+ assert preserve_mode and preserve_times and not preserve_symlinks
+ exclude = self.get_exclusions()
+
+ if not exclude:
+ return _install_lib.copy_tree(self, infile, outfile)
+
+ # Exclude namespace package __init__.py* files from the output
+
+ from setuptools.archive_util import unpack_directory
+ from distutils import log
+
+ outfiles = []
+
+ def pf(src, dst):
+ if dst in exclude:
+ log.warn("Skipping installation of %s (namespace package)",dst)
+ return False
+
+ log.info("copying %s -> %s", src, os.path.dirname(dst))
+ outfiles.append(dst)
+ return dst
+
+ unpack_directory(infile, outfile, pf)
+ return outfiles
+
+ def get_outputs(self):
+ outputs = _install_lib.get_outputs(self)
+ exclude = self.get_exclusions()
+ if exclude:
+ return [f for f in outputs if f not in exclude]
+ return outputs
diff --git a/Lib/setuptools/command/install_scripts.py b/Lib/setuptools/command/install_scripts.py
new file mode 100755
index 0000000..69558bf
--- /dev/null
+++ b/Lib/setuptools/command/install_scripts.py
@@ -0,0 +1,56 @@
+from distutils.command.install_scripts import install_scripts \
+ as _install_scripts
+from easy_install import get_script_args, sys_executable
+from pkg_resources import Distribution, PathMetadata, ensure_directory
+import os
+from distutils import log
+
+class install_scripts(_install_scripts):
+ """Do normal script install, plus any egg_info wrapper scripts"""
+
+ def initialize_options(self):
+ _install_scripts.initialize_options(self)
+ self.no_ep = False
+
+ def run(self):
+ self.run_command("egg_info")
+ if self.distribution.scripts:
+ _install_scripts.run(self) # run first to set up self.outfiles
+ else:
+ self.outfiles = []
+ if self.no_ep:
+ # don't install entry point scripts into .egg file!
+ return
+
+ ei_cmd = self.get_finalized_command("egg_info")
+ dist = Distribution(
+ ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info),
+ ei_cmd.egg_name, ei_cmd.egg_version,
+ )
+ bs_cmd = self.get_finalized_command('build_scripts')
+ executable = getattr(bs_cmd,'executable',sys_executable)
+
+ for args in get_script_args(dist, executable):
+ self.write_script(*args)
+
+
+
+
+
+
+
+ def write_script(self, script_name, contents, mode="t", *ignored):
+ """Write an executable file to the scripts directory"""
+ log.info("Installing %s script to %s", script_name, self.install_dir)
+ target = os.path.join(self.install_dir, script_name)
+ self.outfiles.append(target)
+
+ if not self.dry_run:
+ ensure_directory(target)
+ f = open(target,"w"+mode)
+ f.write(contents)
+ f.close()
+ try:
+ os.chmod(target,0755)
+ except (AttributeError, os.error):
+ pass
diff --git a/Lib/setuptools/command/rotate.py b/Lib/setuptools/command/rotate.py
new file mode 100755
index 0000000..8aab312
--- /dev/null
+++ b/Lib/setuptools/command/rotate.py
@@ -0,0 +1,57 @@
+import distutils, os
+from setuptools import Command
+from distutils.util import convert_path
+from distutils import log
+from distutils.errors import *
+
+class rotate(Command):
+ """Delete older distributions"""
+
+ description = "delete older distributions, keeping N newest files"
+ user_options = [
+ ('match=', 'm', "patterns to match (required)"),
+ ('dist-dir=', 'd', "directory where the distributions are"),
+ ('keep=', 'k', "number of matching distributions to keep"),
+ ]
+
+ boolean_options = []
+
+ def initialize_options(self):
+ self.match = None
+ self.dist_dir = None
+ self.keep = None
+
+ def finalize_options(self):
+ if self.match is None:
+ raise DistutilsOptionError(
+ "Must specify one or more (comma-separated) match patterns "
+ "(e.g. '.zip' or '.egg')"
+ )
+ if self.keep is None:
+ raise DistutilsOptionError("Must specify number of files to keep")
+ try:
+ self.keep = int(self.keep)
+ except ValueError:
+ raise DistutilsOptionError("--keep must be an integer")
+ if isinstance(self.match, basestring):
+ self.match = [
+ convert_path(p.strip()) for p in self.match.split(',')
+ ]
+ self.set_undefined_options('bdist',('dist_dir', 'dist_dir'))
+
+ def run(self):
+ self.run_command("egg_info")
+ from glob import glob
+ for pattern in self.match:
+ pattern = self.distribution.get_name()+'*'+pattern
+ files = glob(os.path.join(self.dist_dir,pattern))
+ files = [(os.path.getmtime(f),f) for f in files]
+ files.sort()
+ files.reverse()
+
+ log.info("%d file(s) matching %s", len(files), pattern)
+ files = files[self.keep:]
+ for (t,f) in files:
+ log.info("Deleting %s", f)
+ if not self.dry_run:
+ os.unlink(f)
diff --git a/Lib/setuptools/command/saveopts.py b/Lib/setuptools/command/saveopts.py
new file mode 100755
index 0000000..9c58d72
--- /dev/null
+++ b/Lib/setuptools/command/saveopts.py
@@ -0,0 +1,24 @@
+import distutils, os
+from setuptools import Command
+from setuptools.command.setopt import edit_config, option_base
+
+class saveopts(option_base):
+ """Save command-line options to a file"""
+
+ description = "save supplied options to setup.cfg or other config file"
+
+ def run(self):
+ dist = self.distribution
+ commands = dist.command_options.keys()
+ settings = {}
+
+ for cmd in commands:
+
+ if cmd=='saveopts':
+ continue # don't save our own options!
+
+ for opt,(src,val) in dist.get_option_dict(cmd).items():
+ if src=="command line":
+ settings.setdefault(cmd,{})[opt] = val
+
+ edit_config(self.filename, settings, self.dry_run)
diff --git a/Lib/setuptools/command/sdist.py b/Lib/setuptools/command/sdist.py
new file mode 100755
index 0000000..829cd3c
--- /dev/null
+++ b/Lib/setuptools/command/sdist.py
@@ -0,0 +1,163 @@
+from distutils.command.sdist import sdist as _sdist
+from distutils.util import convert_path
+import os, re, sys, pkg_resources
+
+entities = [
+ ("&lt;","<"), ("&gt;", ">"), ("&quot;", '"'), ("&apos;", "'"),
+ ("&amp;", "&")
+]
+
+def unescape(data):
+ for old,new in entities:
+ data = data.replace(old,new)
+ return data
+
+def re_finder(pattern, postproc=None):
+ def find(dirname, filename):
+ f = open(filename,'rU')
+ data = f.read()
+ f.close()
+ for match in pattern.finditer(data):
+ path = match.group(1)
+ if postproc:
+ path = postproc(path)
+ yield joinpath(dirname,path)
+ return find
+
+def joinpath(prefix,suffix):
+ if not prefix:
+ return suffix
+ return os.path.join(prefix,suffix)
+
+
+
+
+
+
+
+
+
+
+
+def walk_revctrl(dirname=''):
+ """Find all files under revision control"""
+ for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
+ for item in ep.load()(dirname):
+ yield item
+
+def _default_revctrl(dirname=''):
+ for path, finder in finders:
+ path = joinpath(dirname,path)
+ if os.path.isfile(path):
+ for path in finder(dirname,path):
+ if os.path.isfile(path):
+ yield path
+ elif os.path.isdir(path):
+ for item in _default_revctrl(path):
+ yield item
+
+def externals_finder(dirname, filename):
+ """Find any 'svn:externals' directories"""
+ found = False
+ f = open(filename,'rb')
+ for line in iter(f.readline, ''): # can't use direct iter!
+ parts = line.split()
+ if len(parts)==2:
+ kind,length = parts
+ data = f.read(int(length))
+ if kind=='K' and data=='svn:externals':
+ found = True
+ elif kind=='V' and found:
+ f.close()
+ break
+ else:
+ f.close()
+ return
+
+ for line in data.splitlines():
+ parts = line.split()
+ if parts:
+ yield joinpath(dirname, parts[0])
+
+
+finders = [
+ (convert_path('CVS/Entries'),
+ re_finder(re.compile(r"^\w?/([^/]+)/", re.M))),
+ (convert_path('.svn/entries'),
+ re_finder(
+ re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I),
+ unescape
+ )
+ ),
+ (convert_path('.svn/dir-props'), externals_finder),
+]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class sdist(_sdist):
+ """Smart sdist that finds anything supported by revision control"""
+
+ user_options = [
+ ('formats=', None,
+ "formats for source distribution (comma-separated list)"),
+ ('keep-temp', 'k',
+ "keep the distribution tree around after creating " +
+ "archive file(s)"),
+ ('dist-dir=', 'd',
+ "directory to put the source distribution archive(s) in "
+ "[default: dist]"),
+ ]
+
+ negative_opt = {}
+
+ def run(self):
+ self.run_command('egg_info')
+ ei_cmd = self.get_finalized_command('egg_info')
+ self.filelist = ei_cmd.filelist
+ self.filelist.append(os.path.join(ei_cmd.egg_info,'SOURCES.txt'))
+
+ self.check_metadata()
+ self.make_distribution()
+
+ dist_files = getattr(self.distribution,'dist_files',[])
+ for file in self.archive_files:
+ data = ('sdist', '', file)
+ if data not in dist_files:
+ dist_files.append(data)
+
+ def read_template(self):
+ try:
+ _sdist.read_template(self)
+ except:
+ # grody hack to close the template file (MANIFEST.in)
+ # this prevents easy_install's attempt at deleting the file from
+ # dying and thus masking the real error
+ sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close()
+ raise
diff --git a/Lib/setuptools/command/setopt.py b/Lib/setuptools/command/setopt.py
new file mode 100755
index 0000000..e0c1058
--- /dev/null
+++ b/Lib/setuptools/command/setopt.py
@@ -0,0 +1,158 @@
+import distutils, os
+from setuptools import Command
+from distutils.util import convert_path
+from distutils import log
+from distutils.errors import *
+
+__all__ = ['config_file', 'edit_config', 'option_base', 'setopt']
+
+
+def config_file(kind="local"):
+ """Get the filename of the distutils, local, global, or per-user config
+
+ `kind` must be one of "local", "global", or "user"
+ """
+ if kind=='local':
+ return 'setup.cfg'
+ if kind=='global':
+ return os.path.join(
+ os.path.dirname(distutils.__file__),'distutils.cfg'
+ )
+ if kind=='user':
+ dot = os.name=='posix' and '.' or ''
+ return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot))
+ raise ValueError(
+ "config_file() type must be 'local', 'global', or 'user'", kind
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def edit_config(filename, settings, dry_run=False):
+ """Edit a configuration file to include `settings`
+
+ `settings` is a dictionary of dictionaries or ``None`` values, keyed by
+ command/section name. A ``None`` value means to delete the entire section,
+ while a dictionary lists settings to be changed or deleted in that section.
+ A setting of ``None`` means to delete that setting.
+ """
+ from ConfigParser import RawConfigParser
+ log.debug("Reading configuration from %s", filename)
+ opts = RawConfigParser()
+ opts.read([filename])
+ for section, options in settings.items():
+ if options is None:
+ log.info("Deleting section [%s] from %s", section, filename)
+ opts.remove_section(section)
+ else:
+ if not opts.has_section(section):
+ log.debug("Adding new section [%s] to %s", section, filename)
+ opts.add_section(section)
+ for option,value in options.items():
+ if value is None:
+ log.debug("Deleting %s.%s from %s",
+ section, option, filename
+ )
+ opts.remove_option(section,option)
+ if not opts.options(section):
+ log.info("Deleting empty [%s] section from %s",
+ section, filename)
+ opts.remove_section(section)
+ else:
+ log.debug(
+ "Setting %s.%s to %r in %s",
+ section, option, value, filename
+ )
+ opts.set(section,option,value)
+
+ log.info("Writing %s", filename)
+ if not dry_run:
+ f = open(filename,'w'); opts.write(f); f.close()
+
+class option_base(Command):
+ """Abstract base class for commands that mess with config files"""
+
+ user_options = [
+ ('global-config', 'g',
+ "save options to the site-wide distutils.cfg file"),
+ ('user-config', 'u',
+ "save options to the current user's pydistutils.cfg file"),
+ ('filename=', 'f',
+ "configuration file to use (default=setup.cfg)"),
+ ]
+
+ boolean_options = [
+ 'global-config', 'user-config',
+ ]
+
+ def initialize_options(self):
+ self.global_config = None
+ self.user_config = None
+ self.filename = None
+
+ def finalize_options(self):
+ filenames = []
+ if self.global_config:
+ filenames.append(config_file('global'))
+ if self.user_config:
+ filenames.append(config_file('user'))
+ if self.filename is not None:
+ filenames.append(self.filename)
+ if not filenames:
+ filenames.append(config_file('local'))
+ if len(filenames)>1:
+ raise DistutilsOptionError(
+ "Must specify only one configuration file option",
+ filenames
+ )
+ self.filename, = filenames
+
+
+
+
+class setopt(option_base):
+ """Save command-line options to a file"""
+
+ description = "set an option in setup.cfg or another config file"
+
+ user_options = [
+ ('command=', 'c', 'command to set an option for'),
+ ('option=', 'o', 'option to set'),
+ ('set-value=', 's', 'value of the option'),
+ ('remove', 'r', 'remove (unset) the value'),
+ ] + option_base.user_options
+
+ boolean_options = option_base.boolean_options + ['remove']
+
+ def initialize_options(self):
+ option_base.initialize_options(self)
+ self.command = None
+ self.option = None
+ self.set_value = None
+ self.remove = None
+
+ def finalize_options(self):
+ option_base.finalize_options(self)
+ if self.command is None or self.option is None:
+ raise DistutilsOptionError("Must specify --command *and* --option")
+ if self.set_value is None and not self.remove:
+ raise DistutilsOptionError("Must specify --set-value or --remove")
+
+ def run(self):
+ edit_config(
+ self.filename, {
+ self.command: {self.option.replace('-','_'):self.set_value}
+ },
+ self.dry_run
+ )
diff --git a/Lib/setuptools/command/test.py b/Lib/setuptools/command/test.py
new file mode 100644
index 0000000..01fca35
--- /dev/null
+++ b/Lib/setuptools/command/test.py
@@ -0,0 +1,119 @@
+from setuptools import Command
+from distutils.errors import DistutilsOptionError
+import sys
+from pkg_resources import *
+from unittest import TestLoader, main
+
+class ScanningLoader(TestLoader):
+
+ def loadTestsFromModule(self, module):
+ """Return a suite of all tests cases contained in the given module
+
+ If the module is a package, load tests from all the modules in it.
+ If the module has an ``additional_tests`` function, call it and add
+ the return value to the tests.
+ """
+ tests = []
+ if module.__name__!='setuptools.tests.doctest': # ugh
+ tests.append(TestLoader.loadTestsFromModule(self,module))
+
+ if hasattr(module, "additional_tests"):
+ tests.append(module.additional_tests())
+
+ if hasattr(module, '__path__'):
+ for file in resource_listdir(module.__name__, ''):
+ if file.endswith('.py') and file!='__init__.py':
+ submodule = module.__name__+'.'+file[:-3]
+ else:
+ if resource_exists(
+ module.__name__, file+'/__init__.py'
+ ):
+ submodule = module.__name__+'.'+file
+ else:
+ continue
+ tests.append(self.loadTestsFromName(submodule))
+
+ if len(tests)!=1:
+ return self.suiteClass(tests)
+ else:
+ return tests[0] # don't create a nested suite for only one return
+
+
+class test(Command):
+
+ """Command to run unit tests after in-place build"""
+
+ description = "run unit tests after in-place build"
+
+ user_options = [
+ ('test-module=','m', "Run 'test_suite' in specified module"),
+ ('test-suite=','s',
+ "Test suite to run (e.g. 'some_module.test_suite')"),
+ ]
+
+ def initialize_options(self):
+ self.test_suite = None
+ self.test_module = None
+ self.test_loader = None
+
+
+ def finalize_options(self):
+
+ if self.test_suite is None:
+ if self.test_module is None:
+ self.test_suite = self.distribution.test_suite
+ else:
+ self.test_suite = self.test_module+".test_suite"
+ elif self.test_module:
+ raise DistutilsOptionError(
+ "You may specify a module or a suite, but not both"
+ )
+
+ self.test_args = [self.test_suite]
+
+ if self.verbose:
+ self.test_args.insert(0,'--verbose')
+ if self.test_loader is None:
+ self.test_loader = getattr(self.distribution,'test_loader',None)
+ if self.test_loader is None:
+ self.test_loader = "setuptools.command.test:ScanningLoader"
+
+
+
+ def run(self):
+ # Ensure metadata is up-to-date
+ self.run_command('egg_info')
+
+ # Build extensions in-place
+ self.reinitialize_command('build_ext', inplace=1)
+ self.run_command('build_ext')
+
+ if self.distribution.tests_require:
+ self.distribution.fetch_build_eggs(self.distribution.tests_require)
+
+ if self.test_suite:
+ cmd = ' '.join(self.test_args)
+ if self.dry_run:
+ self.announce('skipping "unittest %s" (dry run)' % cmd)
+ else:
+ self.announce('running "unittest %s"' % cmd)
+ self.run_tests()
+
+
+ def run_tests(self):
+ import unittest
+ old_path = sys.path[:]
+ ei_cmd = self.get_finalized_command("egg_info")
+ path_item = normalize_path(ei_cmd.egg_base)
+ metadata = PathMetadata(
+ path_item, normalize_path(ei_cmd.egg_info)
+ )
+ dist = Distribution(path_item, metadata, project_name=ei_cmd.egg_name)
+ working_set.add(dist)
+ require(str(dist.as_requirement()))
+ loader_ep = EntryPoint.parse("x="+self.test_loader)
+ loader_class = loader_ep.load(require=False)
+ unittest.main(
+ None, None, [unittest.__file__]+self.test_args,
+ testLoader = loader_class()
+ )
diff --git a/Lib/setuptools/command/upload.py b/Lib/setuptools/command/upload.py
new file mode 100755
index 0000000..644c400
--- /dev/null
+++ b/Lib/setuptools/command/upload.py
@@ -0,0 +1,178 @@
+"""distutils.command.upload
+
+Implements the Distutils 'upload' subcommand (upload package to PyPI)."""
+
+from distutils.errors import *
+from distutils.core import Command
+from distutils.spawn import spawn
+from distutils import log
+from md5 import md5
+import os
+import socket
+import platform
+import ConfigParser
+import httplib
+import base64
+import urlparse
+import cStringIO as StringIO
+
+class upload(Command):
+
+ description = "upload binary package to PyPI"
+
+ DEFAULT_REPOSITORY = 'http://www.python.org/pypi'
+
+ user_options = [
+ ('repository=', 'r',
+ "url of repository [default: %s]" % DEFAULT_REPOSITORY),
+ ('show-response', None,
+ 'display full response text from server'),
+ ('sign', 's',
+ 'sign files to upload using gpg'),
+ ('identity=', 'i', 'GPG identity used to sign files'),
+ ]
+ boolean_options = ['show-response', 'sign']
+
+ def initialize_options(self):
+ self.username = ''
+ self.password = ''
+ self.repository = ''
+ self.show_response = 0
+ self.sign = False
+ self.identity = None
+
+ def finalize_options(self):
+ if self.identity and not self.sign:
+ raise DistutilsOptionError(
+ "Must use --sign for --identity to have meaning"
+ )
+ if os.environ.has_key('HOME'):
+ rc = os.path.join(os.environ['HOME'], '.pypirc')
+ if os.path.exists(rc):
+ self.announce('Using PyPI login from %s' % rc)
+ config = ConfigParser.ConfigParser({
+ 'username':'',
+ 'password':'',
+ 'repository':''})
+ config.read(rc)
+ if not self.repository:
+ self.repository = config.get('server-login', 'repository')
+ if not self.username:
+ self.username = config.get('server-login', 'username')
+ if not self.password:
+ self.password = config.get('server-login', 'password')
+ if not self.repository:
+ self.repository = self.DEFAULT_REPOSITORY
+
+ def run(self):
+ if not self.distribution.dist_files:
+ raise DistutilsOptionError("No dist file created in earlier command")
+ for command, pyversion, filename in self.distribution.dist_files:
+ self.upload_file(command, pyversion, filename)
+
+ def upload_file(self, command, pyversion, filename):
+ # Sign if requested
+ if self.sign:
+ gpg_args = ["gpg", "--detach-sign", "-a", filename]
+ if self.identity:
+ gpg_args[2:2] = ["--local-user", self.identity]
+ spawn(gpg_args,
+ dry_run=self.dry_run)
+
+ # Fill in the data
+ content = open(filename,'rb').read()
+ basename = os.path.basename(filename)
+ comment = ''
+ if command=='bdist_egg' and self.distribution.has_ext_modules():
+ comment = "built on %s" % platform.platform(terse=1)
+ data = {
+ ':action':'file_upload',
+ 'protcol_version':'1',
+ 'name':self.distribution.get_name(),
+ 'version':self.distribution.get_version(),
+ 'content':(basename,content),
+ 'filetype':command,
+ 'pyversion':pyversion,
+ 'md5_digest':md5(content).hexdigest(),
+ }
+ if command == 'bdist_rpm':
+ dist, version, id = platform.dist()
+ if dist:
+ comment = 'built for %s %s' % (dist, version)
+ elif command == 'bdist_dumb':
+ comment = 'built for %s' % platform.platform(terse=1)
+ data['comment'] = comment
+
+ if self.sign:
+ data['gpg_signature'] = (os.path.basename(filename) + ".asc",
+ open(filename+".asc").read())
+
+ # set up the authentication
+ auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip()
+
+ # Build up the MIME payload for the POST data
+ boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+ sep_boundary = '\n--' + boundary
+ end_boundary = sep_boundary + '--'
+ body = StringIO.StringIO()
+ for key, value in data.items():
+ # handle multiple entries for the same name
+ if type(value) != type([]):
+ value = [value]
+ for value in value:
+ if type(value) is tuple:
+ fn = ';filename="%s"' % value[0]
+ value = value[1]
+ else:
+ fn = ""
+ value = str(value)
+ body.write(sep_boundary)
+ body.write('\nContent-Disposition: form-data; name="%s"'%key)
+ body.write(fn)
+ body.write("\n\n")
+ body.write(value)
+ if value and value[-1] == '\r':
+ body.write('\n') # write an extra newline (lurve Macs)
+ body.write(end_boundary)
+ body.write("\n")
+ body = body.getvalue()
+
+ self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO)
+
+ # build the Request
+ # We can't use urllib2 since we need to send the Basic
+ # auth right with the first request
+ schema, netloc, url, params, query, fragments = \
+ urlparse.urlparse(self.repository)
+ assert not params and not query and not fragments
+ if schema == 'http':
+ http = httplib.HTTPConnection(netloc)
+ elif schema == 'https':
+ http = httplib.HTTPSConnection(netloc)
+ else:
+ raise AssertionError, "unsupported schema "+schema
+
+ data = ''
+ loglevel = log.INFO
+ try:
+ http.connect()
+ http.putrequest("POST", url)
+ http.putheader('Content-type',
+ 'multipart/form-data; boundary=%s'%boundary)
+ http.putheader('Content-length', str(len(body)))
+ http.putheader('Authorization', auth)
+ http.endheaders()
+ http.send(body)
+ except socket.error, e:
+ self.announce(e.msg, log.ERROR)
+ return
+
+ r = http.getresponse()
+ if r.status == 200:
+ self.announce('Server response (%s): %s' % (r.status, r.reason),
+ log.INFO)
+ else:
+ self.announce('Upload failed (%s): %s' % (r.status, r.reason),
+ log.ERROR)
+ if self.show_response:
+ print '-'*75, r.read(), '-'*75
diff --git a/Lib/setuptools/depends.py b/Lib/setuptools/depends.py
new file mode 100644
index 0000000..68d8194
--- /dev/null
+++ b/Lib/setuptools/depends.py
@@ -0,0 +1,239 @@
+from __future__ import generators
+import sys, imp, marshal
+from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN
+from distutils.version import StrictVersion, LooseVersion
+
+__all__ = [
+ 'Require', 'find_module', 'get_module_constant', 'extract_constant'
+]
+
+class Require:
+ """A prerequisite to building or installing a distribution"""
+
+ def __init__(self,name,requested_version,module,homepage='',
+ attribute=None,format=None
+ ):
+
+ if format is None and requested_version is not None:
+ format = StrictVersion
+
+ if format is not None:
+ requested_version = format(requested_version)
+ if attribute is None:
+ attribute = '__version__'
+
+ self.__dict__.update(locals())
+ del self.self
+
+
+ def full_name(self):
+ """Return full package/distribution name, w/version"""
+ if self.requested_version is not None:
+ return '%s-%s' % (self.name,self.requested_version)
+ return self.name
+
+
+ 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
+
+
+ def get_version(self, paths=None, default="unknown"):
+
+ """Get version number of installed module, 'None', or 'default'
+
+ Search 'paths' for module. If not found, return 'None'. If found,
+ return the extracted version attribute, or 'default' if no version
+ attribute was specified, or the value cannot be determined without
+ importing the module. The version is formatted according to the
+ requirement's version format (if any), unless it is 'None' or the
+ supplied 'default'.
+ """
+
+ if self.attribute is None:
+ try:
+ f,p,i = find_module(self.module,paths)
+ if f: f.close()
+ return default
+ except ImportError:
+ return None
+
+ v = get_module_constant(self.module,self.attribute,default,paths)
+
+ if v is not None and v is not default and self.format is not None:
+ return self.format(v)
+
+ return v
+
+
+ def is_present(self,paths=None):
+ """Return true if dependency is present on 'paths'"""
+ return self.get_version(paths) is not None
+
+
+ def is_current(self,paths=None):
+ """Return true if dependency is present and up-to-date on 'paths'"""
+ version = self.get_version(paths)
+ if version is None:
+ return False
+ return self.version_ok(version)
+
+
+def _iter_code(code):
+
+ """Yield '(op,arg)' pair for each operation in code object 'code'"""
+
+ from array import array
+ from dis import HAVE_ARGUMENT, EXTENDED_ARG
+
+ bytes = array('b',code.co_code)
+ eof = len(code.co_code)
+
+ ptr = 0
+ extended_arg = 0
+
+ while ptr<eof:
+
+ op = bytes[ptr]
+
+ if op>=HAVE_ARGUMENT:
+
+ arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg
+ ptr += 3
+
+ if op==EXTENDED_ARG:
+ extended_arg = arg * 65536L
+ continue
+
+ else:
+ arg = None
+ ptr += 1
+
+ yield op,arg
+
+
+
+
+
+
+
+
+
+
+def find_module(module, paths=None):
+ """Just like 'imp.find_module()', but with package support"""
+
+ parts = module.split('.')
+
+ while parts:
+ part = parts.pop(0)
+ f, path, (suffix,mode,kind) = info = imp.find_module(part, paths)
+
+ if kind==PKG_DIRECTORY:
+ parts = parts or ['__init__']
+ paths = [path]
+
+ elif parts:
+ raise ImportError("Can't find %r in %s" % (parts,module))
+
+ return info
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def get_module_constant(module, symbol, default=-1, paths=None):
+
+ """Find 'module' by searching 'paths', and extract 'symbol'
+
+ Return 'None' if 'module' does not exist on 'paths', or it does not define
+ 'symbol'. If the module defines 'symbol' as a constant, return the
+ constant. Otherwise, return 'default'."""
+
+ try:
+ f, path, (suffix,mode,kind) = find_module(module,paths)
+ except ImportError:
+ # Module doesn't exist
+ return None
+
+ try:
+ if kind==PY_COMPILED:
+ f.read(8) # skip magic & date
+ code = marshal.load(f)
+ elif kind==PY_FROZEN:
+ code = imp.get_frozen_object(module)
+ elif kind==PY_SOURCE:
+ code = compile(f.read(), path, 'exec')
+ else:
+ # Not something we can parse; we'll have to import it. :(
+ if module not in sys.modules:
+ imp.load_module(module,f,path,(suffix,mode,kind))
+ return getattr(sys.modules[module],symbol,None)
+
+ finally:
+ if f:
+ f.close()
+
+ return extract_constant(code,symbol,default)
+
+
+
+
+
+
+
+
+def extract_constant(code,symbol,default=-1):
+
+ """Extract the constant value of 'symbol' from 'code'
+
+ If the name 'symbol' is bound to a constant value by the Python code
+ object 'code', return that value. If 'symbol' is bound to an expression,
+ return 'default'. Otherwise, return 'None'.
+
+ Return value is based on the first assignment to 'symbol'. 'symbol' must
+ be a global, or at least a non-"fast" local in the code block. That is,
+ only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
+ must be present in 'code.co_names'.
+ """
+
+ if symbol not in code.co_names:
+ # name's not there, can't possibly be an assigment
+ return None
+
+ name_idx = list(code.co_names).index(symbol)
+
+ STORE_NAME = 90
+ STORE_GLOBAL = 97
+ LOAD_CONST = 100
+
+ const = default
+
+ for op, arg in _iter_code(code):
+
+ if op==LOAD_CONST:
+ const = code.co_consts[arg]
+ elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL):
+ return const
+ else:
+ const = default
diff --git a/Lib/setuptools/dist.py b/Lib/setuptools/dist.py
new file mode 100644
index 0000000..f0417c1
--- /dev/null
+++ b/Lib/setuptools/dist.py
@@ -0,0 +1,798 @@
+__all__ = ['Distribution']
+
+from distutils.core import Distribution as _Distribution
+from setuptools.depends import Require
+from setuptools.command.install import install
+from setuptools.command.sdist import sdist
+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
+
+def _get_unpatched(cls):
+ """Protect against re-patching the distutils if reloaded
+
+ Also ensures that no other distutils extension monkeypatched the distutils
+ first.
+ """
+ while cls.__module__.startswith('setuptools'):
+ cls, = cls.__bases__
+ if not cls.__module__.startswith('distutils'):
+ raise AssertionError(
+ "distutils has already been patched by %r" % cls
+ )
+ return cls
+
+_Distribution = _get_unpatched(_Distribution)
+
+sequence = tuple, list
+
+def check_importable(dist, attr, value):
+ try:
+ ep = pkg_resources.EntryPoint.parse('x='+value)
+ assert not ep.extras
+ except (TypeError,ValueError,AttributeError,AssertionError):
+ raise DistutilsSetupError(
+ "%r must be importable 'module:attrs' string (got %r)"
+ % (attr,value)
+ )
+
+
+def assert_string_list(dist, attr, value):
+ """Verify that value is a string list or None"""
+ try:
+ assert ''.join(value)!=value
+ except (TypeError,ValueError,AttributeError,AssertionError):
+ raise DistutilsSetupError(
+ "%r must be a list of strings (got %r)" % (attr,value)
+ )
+
+def check_nsp(dist, attr, value):
+ """Verify that namespace packages are valid"""
+ assert_string_list(dist,attr,value)
+
+ for nsp in value:
+ if not dist.has_contents_for(nsp):
+ raise DistutilsSetupError(
+ "Distribution contains no modules or packages for " +
+ "namespace package %r" % nsp
+ )
+
+def check_extras(dist, attr, value):
+ """Verify that extras_require mapping is valid"""
+ try:
+ for k,v in value.items():
+ list(pkg_resources.parse_requirements(v))
+ except (TypeError,ValueError,AttributeError):
+ raise DistutilsSetupError(
+ "'extras_require' must be a dictionary whose values are "
+ "strings or lists of strings containing valid project/version "
+ "requirement specifiers."
+ )
+
+def assert_bool(dist, attr, value):
+ """Verify that value is True, False, 0, or 1"""
+ if bool(value) != value:
+ raise DistutilsSetupError(
+ "%r must be a boolean value (got %r)" % (attr,value)
+ )
+
+
+
+def check_requirements(dist, attr, value):
+ """Verify that install_requires is a valid requirements list"""
+ try:
+ list(pkg_resources.parse_requirements(value))
+ except (TypeError,ValueError):
+ raise DistutilsSetupError(
+ "%r must be a string or list of strings "
+ "containing valid project/version requirement specifiers" % (attr,)
+ )
+
+def check_entry_points(dist, attr, value):
+ """Verify that entry_points map is parseable"""
+ try:
+ pkg_resources.EntryPoint.parse_map(value)
+ except ValueError, e:
+ raise DistutilsSetupError(e)
+
+
+def check_test_suite(dist, attr, value):
+ if not isinstance(value,basestring):
+ raise DistutilsSetupError("test_suite must be a string")
+
+
+def check_package_data(dist, attr, value):
+ """Verify that value is a dictionary of package names to glob lists"""
+ if isinstance(value,dict):
+ for k,v in value.items():
+ if not isinstance(k,str): break
+ try: iter(v)
+ except TypeError:
+ break
+ else:
+ return
+ raise DistutilsSetupError(
+ attr+" must be a dictionary mapping package names to lists of "
+ "wildcard patterns"
+ )
+
+
+
+
+class Distribution(_Distribution):
+ """Distribution with support for features, tests, and package data
+
+ This is an enhanced version of 'distutils.dist.Distribution' that
+ effectively adds the following new optional keyword arguments to 'setup()':
+
+ 'install_requires' -- a string or sequence of strings specifying project
+ versions that the distribution requires when installed, in the format
+ used by 'pkg_resources.require()'. They will be installed
+ automatically when the package is installed. If you wish to use
+ packages that are not available in PyPI, or want to give your users an
+ alternate download location, you can add a 'find_links' option to the
+ '[easy_install]' section of your project's 'setup.cfg' file, and then
+ setuptools will scan the listed web pages for links that satisfy the
+ requirements.
+
+ 'extras_require' -- a dictionary mapping names of optional "extras" to the
+ additional requirement(s) that using those extras incurs. For example,
+ this::
+
+ extras_require = dict(reST = ["docutils>=0.3", "reSTedit"])
+
+ indicates that the distribution can optionally provide an extra
+ capability called "reST", but it can only be used if docutils and
+ reSTedit are installed. If the user installs your package using
+ EasyInstall and requests one of your extras, the corresponding
+ additional requirements will be installed if needed.
+
+ 'features' -- a dictionary mapping option names to 'setuptools.Feature'
+ objects. Features are a portion of the distribution that can be
+ included or excluded based on user options, inter-feature dependencies,
+ and availability on the current system. Excluded features are omitted
+ from all setup commands, including source and binary distributions, so
+ you can create multiple distributions from the same source tree.
+ Feature names should be valid Python identifiers, except that they may
+ contain the '-' (minus) sign. Features can be included or excluded
+ via the command line options '--with-X' and '--without-X', where 'X' is
+ the name of the feature. Whether a feature is included by default, and
+ whether you are allowed to control this from the command line, is
+ determined by the Feature object. See the 'Feature' class for more
+ information.
+
+ 'test_suite' -- the name of a test suite to run for the 'test' command.
+ If the user runs 'python setup.py test', the package will be installed,
+ and the named test suite will be run. The format is the same as
+ would be used on a 'unittest.py' command line. That is, it is the
+ dotted name of an object to import and call to generate a test suite.
+
+ 'package_data' -- a dictionary mapping package names to lists of filenames
+ or globs to use to find data files contained in the named packages.
+ If the dictionary has filenames or globs listed under '""' (the empty
+ string), those names will be searched for in every package, in addition
+ to any names for the specific package. Data files found using these
+ names/globs will be installed along with the package, in the same
+ location as the package. Note that globs are allowed to reference
+ the contents of non-package subdirectories, as long as you use '/' as
+ a path separator. (Globs are automatically converted to
+ platform-specific paths at runtime.)
+
+ In addition to these new keywords, this class also has several new methods
+ for manipulating the distribution's contents. For example, the 'include()'
+ and 'exclude()' methods can be thought of as in-place add and subtract
+ commands that add or remove packages, modules, extensions, and so on from
+ the distribution. They are used by the feature subsystem to configure the
+ distribution for the included and excluded features.
+ """
+
+ _patched_dist = None
+
+ def patch_missing_pkg_info(self, attrs):
+ # Fake up a replacement for the data that would normally come from
+ # PKG-INFO, but which might not yet be built if this is a fresh
+ # checkout.
+ #
+ if not attrs or 'name' not in attrs or 'version' not in attrs:
+ return
+ key = pkg_resources.safe_name(str(attrs['name'])).lower()
+ dist = pkg_resources.working_set.by_key.get(key)
+ if dist is not None and not dist.has_metadata('PKG-INFO'):
+ dist._version = pkg_resources.safe_version(str(attrs['version']))
+ self._patched_dist = dist
+
+ def __init__ (self, attrs=None):
+ have_package_data = hasattr(self, "package_data")
+ if not have_package_data:
+ self.package_data = {}
+ self.require_features = []
+ self.features = {}
+ self.dist_files = []
+ self.patch_missing_pkg_info(attrs)
+ # Make sure we have any eggs needed to interpret 'attrs'
+ if attrs and 'dependency_links' in attrs:
+ self.dependency_links = attrs.pop('dependency_links')
+ assert_string_list(self,'dependency_links',self.dependency_links)
+ if attrs and 'setup_requires' in attrs:
+ self.fetch_build_eggs(attrs.pop('setup_requires'))
+ for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
+ if not hasattr(self,ep.name):
+ setattr(self,ep.name,None)
+ _Distribution.__init__(self,attrs)
+ if isinstance(self.metadata.version, (int,long,float)):
+ # Some people apparently take "version number" too literally :)
+ self.metadata.version = str(self.metadata.version)
+
+ def parse_command_line(self):
+ """Process features after parsing command line options"""
+ result = _Distribution.parse_command_line(self)
+ if self.features:
+ self._finalize_features()
+ return result
+
+ def _feature_attrname(self,name):
+ """Convert feature name to corresponding option attribute name"""
+ return 'with_'+name.replace('-','_')
+
+ def fetch_build_eggs(self, requires):
+ """Resolve pre-setup requirements"""
+ from pkg_resources import working_set, parse_requirements
+ for dist in working_set.resolve(
+ parse_requirements(requires), installer=self.fetch_build_egg
+ ):
+ working_set.add(dist)
+
+ def finalize_options(self):
+ _Distribution.finalize_options(self)
+ if self.features:
+ self._set_global_opts_from_features()
+
+ for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
+ value = getattr(self,ep.name,None)
+ if value is not None:
+ ep.require(installer=self.fetch_build_egg)
+ ep.load()(self, ep.name, value)
+
+ def fetch_build_egg(self, req):
+ """Fetch an egg needed for building"""
+ try:
+ cmd = self._egg_fetcher
+ except AttributeError:
+ from setuptools.command.easy_install import easy_install
+ dist = self.__class__({'script_args':['easy_install']})
+ dist.parse_config_files()
+ opts = dist.get_option_dict('easy_install')
+ keep = (
+ 'find_links', 'site_dirs', 'index_url', 'optimize',
+ 'site_dirs', 'allow_hosts'
+ )
+ for key in opts.keys():
+ if key not in keep:
+ del opts[key] # don't use any other settings
+ if self.dependency_links:
+ links = self.dependency_links[:]
+ if 'find_links' in opts:
+ links = opts['find_links'][1].split() + links
+ opts['find_links'] = ('setup', links)
+ cmd = easy_install(
+ dist, args=["x"], install_dir=os.curdir, exclude_scripts=True,
+ always_copy=False, build_directory=None, editable=False,
+ upgrade=False, multi_version=True, no_report = True
+ )
+ cmd.ensure_finalized()
+ self._egg_fetcher = cmd
+ return cmd.easy_install(req)
+
+ def _set_global_opts_from_features(self):
+ """Add --with-X/--without-X options based on optional features"""
+
+ go = []
+ no = self.negative_opt.copy()
+
+ for name,feature in self.features.items():
+ self._set_feature(name,None)
+ feature.validate(self)
+
+ if feature.optional:
+ descr = feature.description
+ incdef = ' (default)'
+ excdef=''
+ if not feature.include_by_default():
+ excdef, incdef = incdef, excdef
+
+ go.append(('with-'+name, None, 'include '+descr+incdef))
+ go.append(('without-'+name, None, 'exclude '+descr+excdef))
+ no['without-'+name] = 'with-'+name
+
+ self.global_options = self.feature_options = go + self.global_options
+ self.negative_opt = self.feature_negopt = no
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ def _finalize_features(self):
+ """Add/remove features and resolve dependencies between them"""
+
+ # First, flag all the enabled items (and thus their dependencies)
+ for name,feature in self.features.items():
+ enabled = self.feature_is_included(name)
+ if enabled or (enabled is None and feature.include_by_default()):
+ feature.include_in(self)
+ self._set_feature(name,1)
+
+ # Then disable the rest, so that off-by-default features don't
+ # get flagged as errors when they're required by an enabled feature
+ for name,feature in self.features.items():
+ if not self.feature_is_included(name):
+ feature.exclude_from(self)
+ self._set_feature(name,0)
+
+
+ def get_command_class(self, command):
+ """Pluggable version of get_command_class()"""
+ if command in self.cmdclass:
+ return self.cmdclass[command]
+
+ for ep in pkg_resources.iter_entry_points('distutils.commands',command):
+ ep.require(installer=self.fetch_build_egg)
+ self.cmdclass[command] = cmdclass = ep.load()
+ return cmdclass
+ else:
+ return _Distribution.get_command_class(self, command)
+
+ def print_commands(self):
+ for ep in pkg_resources.iter_entry_points('distutils.commands'):
+ if ep.name not in self.cmdclass:
+ cmdclass = ep.load(False) # don't require extras, we're not running
+ self.cmdclass[ep.name] = cmdclass
+ return _Distribution.print_commands(self)
+
+
+
+
+
+ def _set_feature(self,name,status):
+ """Set feature's inclusion status"""
+ setattr(self,self._feature_attrname(name),status)
+
+ def feature_is_included(self,name):
+ """Return 1 if feature is included, 0 if excluded, 'None' if unknown"""
+ return getattr(self,self._feature_attrname(name))
+
+ def include_feature(self,name):
+ """Request inclusion of feature named 'name'"""
+
+ if self.feature_is_included(name)==0:
+ descr = self.features[name].description
+ raise DistutilsOptionError(
+ descr + " is required, but was excluded or is not available"
+ )
+ self.features[name].include_in(self)
+ self._set_feature(name,1)
+
+ def include(self,**attrs):
+ """Add items to distribution that are named in keyword arguments
+
+ For example, 'dist.exclude(py_modules=["x"])' would add 'x' to
+ the distribution's 'py_modules' attribute, if it was not already
+ there.
+
+ Currently, this method only supports inclusion for attributes that are
+ lists or tuples. If you need to add support for adding to other
+ attributes in this or a subclass, you can add an '_include_X' method,
+ where 'X' is the name of the attribute. The method will be called with
+ the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})'
+ will try to call 'dist._include_foo({"bar":"baz"})', which can then
+ handle whatever special inclusion logic is needed.
+ """
+ for k,v in attrs.items():
+ include = getattr(self, '_include_'+k, None)
+ if include:
+ include(v)
+ else:
+ self._include_misc(k,v)
+
+ def exclude_package(self,package):
+ """Remove packages, modules, and extensions in named package"""
+
+ pfx = package+'.'
+ if self.packages:
+ self.packages = [
+ p for p in self.packages
+ 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 self.ext_modules:
+ self.ext_modules = [
+ p for p in self.ext_modules
+ if p.name<>package and not p.name.startswith(pfx)
+ ]
+
+
+ def has_contents_for(self,package):
+ """Return true if 'exclude_package(package)' would do something"""
+
+ pfx = package+'.'
+
+ for p in self.iter_distribution_names():
+ if p==package or p.startswith(pfx):
+ return True
+
+
+
+
+
+
+
+
+
+
+ def _exclude_misc(self,name,value):
+ """Handle 'exclude()' for list/tuple attrs without a special handler"""
+ if not isinstance(value,sequence):
+ raise DistutilsSetupError(
+ "%s: setting must be a list or tuple (%r)" % (name, value)
+ )
+ try:
+ old = getattr(self,name)
+ except AttributeError:
+ raise DistutilsSetupError(
+ "%s: No such distribution setting" % name
+ )
+ if old is not None and not isinstance(old,sequence):
+ raise DistutilsSetupError(
+ name+": this setting cannot be changed via include/exclude"
+ )
+ elif old:
+ setattr(self,name,[item for item in old if item not in value])
+
+ def _include_misc(self,name,value):
+ """Handle 'include()' for list/tuple attrs without a special handler"""
+
+ if not isinstance(value,sequence):
+ raise DistutilsSetupError(
+ "%s: setting must be a list (%r)" % (name, value)
+ )
+ try:
+ old = getattr(self,name)
+ except AttributeError:
+ raise DistutilsSetupError(
+ "%s: No such distribution setting" % name
+ )
+ if old is None:
+ setattr(self,name,value)
+ elif not isinstance(old,sequence):
+ raise DistutilsSetupError(
+ name+": this setting cannot be changed via include/exclude"
+ )
+ else:
+ setattr(self,name,old+[item for item in value if item not in old])
+
+ def exclude(self,**attrs):
+ """Remove items from distribution that are named in keyword arguments
+
+ For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from
+ the distribution's 'py_modules' attribute. Excluding packages uses
+ the 'exclude_package()' method, so all of the package's contained
+ packages, modules, and extensions are also excluded.
+
+ Currently, this method only supports exclusion from attributes that are
+ lists or tuples. If you need to add support for excluding from other
+ attributes in this or a subclass, you can add an '_exclude_X' method,
+ where 'X' is the name of the attribute. The method will be called with
+ the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})'
+ will try to call 'dist._exclude_foo({"bar":"baz"})', which can then
+ handle whatever special exclusion logic is needed.
+ """
+ for k,v in attrs.items():
+ exclude = getattr(self, '_exclude_'+k, None)
+ if exclude:
+ exclude(v)
+ else:
+ self._exclude_misc(k,v)
+
+ def _exclude_packages(self,packages):
+ if not isinstance(packages,sequence):
+ raise DistutilsSetupError(
+ "packages: setting must be a list or tuple (%r)" % (packages,)
+ )
+ map(self.exclude_package, packages)
+
+
+
+
+
+
+
+
+
+
+
+
+ def _parse_command_opts(self, parser, args):
+ # Remove --with-X/--without-X options when processing command args
+ self.global_options = self.__class__.global_options
+ self.negative_opt = self.__class__.negative_opt
+
+ # First, expand any aliases
+ command = args[0]
+ aliases = self.get_option_dict('aliases')
+ while command in aliases:
+ src,alias = aliases[command]
+ del aliases[command] # ensure each alias can expand only once!
+ import shlex
+ args[:1] = shlex.split(alias,True)
+ command = args[0]
+
+ nargs = _Distribution._parse_command_opts(self, parser, args)
+
+ # Handle commands that want to consume all remaining arguments
+ cmd_class = self.get_command_class(command)
+ if getattr(cmd_class,'command_consumes_arguments',None):
+ self.get_option_dict(command)['args'] = ("command line", nargs)
+ if nargs is not None:
+ return []
+
+ return nargs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ def get_cmdline_options(self):
+ """Return a '{cmd: {opt:val}}' map of all command-line options
+
+ Option names are all long, but do not include the leading '--', and
+ contain dashes rather than underscores. If the option doesn't take
+ an argument (e.g. '--quiet'), the 'val' is 'None'.
+
+ Note that options provided by config files are intentionally excluded.
+ """
+
+ d = {}
+
+ for cmd,opts in self.command_options.items():
+
+ for opt,(src,val) in opts.items():
+
+ if src != "command line":
+ continue
+
+ opt = opt.replace('_','-')
+
+ if val==0:
+ cmdobj = self.get_command_obj(cmd)
+ neg_opt = self.negative_opt.copy()
+ neg_opt.update(getattr(cmdobj,'negative_opt',{}))
+ for neg,pos in neg_opt.items():
+ if pos==opt:
+ opt=neg
+ val=None
+ break
+ else:
+ raise AssertionError("Shouldn't be able to get here")
+
+ elif val==1:
+ val = None
+
+ d.setdefault(cmd,{})[opt] = val
+
+ return d
+
+
+ def iter_distribution_names(self):
+ """Yield all packages, modules, and extension names in distribution"""
+
+ for pkg in self.packages or ():
+ yield pkg
+
+ for module in self.py_modules or ():
+ yield module
+
+ for ext in self.ext_modules or ():
+ if isinstance(ext,tuple):
+ name,buildinfo = ext
+ yield name
+ else:
+ yield ext.name
+
+# Install it throughout the distutils
+for module in distutils.dist, distutils.core, distutils.cmd:
+ module.Distribution = Distribution
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class Feature:
+ """A subset of the distribution that can be excluded if unneeded/wanted
+
+ Features are created using these keyword arguments:
+
+ 'description' -- a short, human readable description of the feature, to
+ be used in error messages, and option help messages.
+
+ 'standard' -- if true, the feature is included by default if it is
+ available on the current system. Otherwise, the feature is only
+ included if requested via a command line '--with-X' option, or if
+ another included feature requires it. The default setting is 'False'.
+
+ 'available' -- if true, the feature is available for installation on the
+ current system. The default setting is 'True'.
+
+ 'optional' -- if true, the feature's inclusion can be controlled from the
+ command line, using the '--with-X' or '--without-X' options. If
+ false, the feature's inclusion status is determined automatically,
+ based on 'availabile', 'standard', and whether any other feature
+ requires it. The default setting is 'True'.
+
+ 'require_features' -- a string or sequence of strings naming features
+ that should also be included if this feature is included. Defaults to
+ empty list. May also contain 'Require' objects that should be
+ added/removed from the distribution.
+
+ 'remove' -- a string or list of strings naming packages to be removed
+ from the distribution if this feature is *not* included. If the
+ feature *is* included, this argument is ignored. This argument exists
+ to support removing features that "crosscut" a distribution, such as
+ defining a 'tests' feature that removes all the 'tests' subpackages
+ provided by other features. The default for this argument is an empty
+ list. (Note: the named package(s) or modules must exist in the base
+ distribution when the 'setup()' function is initially called.)
+
+ other keywords -- any other keyword arguments are saved, and passed to
+ the distribution's 'include()' and 'exclude()' methods when the
+ feature is included or excluded, respectively. So, for example, you
+ could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be
+ added or removed from the distribution as appropriate.
+
+ A feature must include at least one 'requires', 'remove', or other
+ keyword argument. Otherwise, it can't affect the distribution in any way.
+ Note also that you can subclass 'Feature' to create your own specialized
+ feature types that modify the distribution in other ways when included or
+ excluded. See the docstrings for the various methods here for more detail.
+ Aside from the methods, the only feature attributes that distributions look
+ at are 'description' and 'optional'.
+ """
+ def __init__(self, description, standard=False, available=True,
+ optional=True, require_features=(), remove=(), **extras
+ ):
+
+ self.description = description
+ self.standard = standard
+ self.available = available
+ self.optional = optional
+ if isinstance(require_features,(str,Require)):
+ require_features = require_features,
+
+ self.require_features = [
+ r for r in require_features if isinstance(r,str)
+ ]
+ er = [r for r in require_features if not isinstance(r,str)]
+ if er: extras['require_features'] = er
+
+ if isinstance(remove,str):
+ remove = remove,
+ self.remove = remove
+ self.extras = extras
+
+ if not remove and not require_features and not extras:
+ raise DistutilsSetupError(
+ "Feature %s: must define 'require_features', 'remove', or at least one"
+ " of 'packages', 'py_modules', etc."
+ )
+
+ def include_by_default(self):
+ """Should this feature be included by default?"""
+ return self.available and self.standard
+
+ def include_in(self,dist):
+
+ """Ensure feature and its requirements are included in distribution
+
+ You may override this in a subclass to perform additional operations on
+ the distribution. Note that this method may be called more than once
+ per feature, and so should be idempotent.
+
+ """
+
+ if not self.available:
+ raise DistutilsPlatformError(
+ self.description+" is required,"
+ "but is not available on this platform"
+ )
+
+ dist.include(**self.extras)
+
+ for f in self.require_features:
+ dist.include_feature(f)
+
+
+
+ def exclude_from(self,dist):
+
+ """Ensure feature is excluded from distribution
+
+ You may override this in a subclass to perform additional operations on
+ the distribution. This method will be called at most once per
+ feature, and only after all included features have been asked to
+ include themselves.
+ """
+
+ dist.exclude(**self.extras)
+
+ if self.remove:
+ for item in self.remove:
+ dist.exclude_package(item)
+
+
+
+ def validate(self,dist):
+
+ """Verify that feature makes sense in context of distribution
+
+ This method is called by the distribution just before it parses its
+ command line. It checks to ensure that the 'remove' attribute, if any,
+ contains only valid package/module names that are present in the base
+ distribution when 'setup()' is called. You may override it in a
+ subclass to perform any other required validation of the feature
+ against a target distribution.
+ """
+
+ for item in self.remove:
+ if not dist.has_contents_for(item):
+ raise DistutilsSetupError(
+ "%s wants to be able to remove %s, but the distribution"
+ " doesn't contain any packages or modules under %s"
+ % (self.description, item, item)
+ )
diff --git a/Lib/setuptools/extension.py b/Lib/setuptools/extension.py
new file mode 100644
index 0000000..cfcf55b
--- /dev/null
+++ b/Lib/setuptools/extension.py
@@ -0,0 +1,35 @@
+from distutils.core import Extension as _Extension
+from dist import _get_unpatched
+_Extension = _get_unpatched(_Extension)
+
+try:
+ from Pyrex.Distutils.build_ext import build_ext
+except ImportError:
+ have_pyrex = False
+else:
+ have_pyrex = True
+
+
+class Extension(_Extension):
+ """Extension that uses '.c' files in place of '.pyx' files"""
+
+ if not have_pyrex:
+ # convert .pyx extensions to .c
+ def __init__(self,*args,**kw):
+ _Extension.__init__(self,*args,**kw)
+ sources = []
+ for s in self.sources:
+ if s.endswith('.pyx'):
+ sources.append(s[:-3]+'c')
+ else:
+ sources.append(s)
+ self.sources = sources
+
+class Library(Extension):
+ """Just like a regular Extension, but built as a library instead"""
+
+import sys, distutils.core, distutils.extension
+distutils.core.Extension = Extension
+distutils.extension.Extension = Extension
+if 'distutils.command.build_ext' in sys.modules:
+ sys.modules['distutils.command.build_ext'].Extension = Extension
diff --git a/Lib/setuptools/gui.exe b/Lib/setuptools/gui.exe
new file mode 100755
index 0000000..63ff35f
--- /dev/null
+++ b/Lib/setuptools/gui.exe
Binary files differ
diff --git a/Lib/setuptools/package_index.py b/Lib/setuptools/package_index.py
new file mode 100755
index 0000000..107e222
--- /dev/null
+++ b/Lib/setuptools/package_index.py
@@ -0,0 +1,674 @@
+"""PyPI and direct package downloading"""
+
+import sys, os.path, re, urlparse, urllib2, shutil, random, socket
+from pkg_resources import *
+from distutils import log
+from distutils.errors import DistutilsError
+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
+PYPI_MD5 = re.compile(
+ '<a href="([^"#]+)">([^<]+)</a>\n\s+\\(<a href="[^?]+\?:action=show_md5'
+ '&amp;digest=([0-9a-f]{32})">md5</a>\\)'
+)
+
+URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match
+EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
+
+__all__ = [
+ 'PackageIndex', 'distros_for_url', 'parse_bdist_wininst',
+ 'interpret_distro_name',
+]
+
+
+def parse_bdist_wininst(name):
+ """Return (base,pyversion) or (None,None) for possible .exe name"""
+
+ lower = name.lower()
+ base, py_ver = None, None
+
+ if lower.endswith('.exe'):
+ if lower.endswith('.win32.exe'):
+ base = name[:-10]
+ elif lower.startswith('.win32-py',-16):
+ py_ver = name[-7:-4]
+ base = name[:-16]
+
+ return base,py_ver
+
+def egg_info_for_url(url):
+ scheme, server, path, parameters, query, fragment = urlparse.urlparse(url)
+ base = urllib2.unquote(path.split('/')[-1])
+ if '#' in base: base, fragment = base.split('#',1)
+ return base,fragment
+
+def distros_for_url(url, metadata=None):
+ """Yield egg or source distribution objects that might be found at a URL"""
+ base, fragment = egg_info_for_url(url)
+ dists = distros_for_location(url, base, metadata)
+ if fragment and not dists:
+ match = EGG_FRAGMENT.match(fragment)
+ if match:
+ return interpret_distro_name(
+ url, match.group(1), metadata, precedence = CHECKOUT_DIST
+ )
+ return dists
+
+def distros_for_location(location, basename, metadata=None):
+ """Yield egg or source distribution objects based on basename"""
+ if basename.endswith('.egg.zip'):
+ basename = basename[:-4] # strip the .zip
+ if basename.endswith('.egg'): # 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:
+ if basename.endswith(ext):
+ basename = basename[:-len(ext)]
+ return interpret_distro_name(location, basename, metadata)
+ return [] # no extension matched
+
+
+def distros_for_filename(filename, metadata=None):
+ """Yield possible egg or source distribution objects based on a filename"""
+ return distros_for_location(
+ normalize_path(filename), os.path.basename(filename), metadata
+ )
+
+
+def interpret_distro_name(location, basename, metadata,
+ py_version=None, precedence=SOURCE_DIST, platform=None
+):
+ """Generate alternative interpretations of a source distro name
+
+ Note: if `location` is a filesystem filename, you should call
+ ``pkg_resources.normalize_path()`` on it before passing it to this
+ routine!
+ """
+
+ # Generate alternative interpretations of a source distro name
+ # Because some packages are ambiguous as to name/versions split
+ # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc.
+ # So, we generate each possible interepretation (e.g. "adns, python-1.1.0"
+ # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice,
+ # the spurious interpretations should be ignored, because in the event
+ # there's also an "adns" package, the spurious "python-1.1.0" version will
+ # compare lower than any numeric version number, and is therefore unlikely
+ # to match a request for it. It's still a potential problem, though, and
+ # in the long run PyPI and the distutils should go for "safe" names and
+ # versions in distribution archive names (sdist and bdist).
+
+ parts = basename.split('-')
+ for p in range(1,len(parts)+1):
+ yield Distribution(
+ location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]),
+ py_version=py_version, precedence = precedence,
+ platform = platform
+ )
+
+
+
+
+
+class PackageIndex(Environment):
+ """A distribution index that scans web pages for download URLs"""
+
+ def __init__(self,index_url="http://www.python.org/pypi",hosts=('*',),*args,**kw):
+ Environment.__init__(self,*args,**kw)
+ self.index_url = index_url + "/"[:not index_url.endswith('/')]
+ self.scanned_urls = {}
+ self.fetched_urls = {}
+ self.package_pages = {}
+ self.allows = re.compile('|'.join(map(translate,hosts))).match
+ self.to_scan = []
+
+ def process_url(self, url, retrieve=False):
+ """Evaluate a URL as a possible download, and maybe retrieve it"""
+ url = fix_sf_url(url)
+ if url in self.scanned_urls and not retrieve:
+ return
+ self.scanned_urls[url] = True
+ if not URL_SCHEME(url):
+ self.process_filename(url)
+ return
+ else:
+ dists = list(distros_for_url(url))
+ if dists:
+ if not self.url_ok(url):
+ return
+ self.debug("Found link: %s", url)
+
+ if dists or not retrieve or url in self.fetched_urls:
+ map(self.add, dists)
+ return # don't need the actual page
+
+ if not self.url_ok(url):
+ self.fetched_urls[url] = True
+ return
+
+ self.info("Reading %s", url)
+ f = self.open_url(url)
+ self.fetched_urls[url] = self.fetched_urls[f.url] = True
+
+
+ if 'html' not in f.headers['content-type'].lower():
+ f.close() # not html, we can't process it
+ return
+
+ base = f.url # handle redirects
+ page = f.read()
+ f.close()
+ if url.startswith(self.index_url):
+ page = self.process_index(url, page)
+
+ for match in HREF.finditer(page):
+ link = urlparse.urljoin(base, match.group(1))
+ self.process_url(link)
+
+ def process_filename(self, fn, nested=False):
+ # process filenames or directories
+ if not os.path.exists(fn):
+ self.warn("Not found: %s", url)
+ return
+
+ if os.path.isdir(fn) and not nested:
+ path = os.path.realpath(fn)
+ for item in os.listdir(path):
+ self.process_filename(os.path.join(path,item), True)
+
+ dists = distros_for_filename(fn)
+ if dists:
+ self.debug("Found: %s", fn)
+ map(self.add, dists)
+
+ def url_ok(self, url, fatal=False):
+ if self.allows(urlparse.urlparse(url)[1]):
+ return True
+ msg = "\nLink to % s ***BLOCKED*** by --allow-hosts\n"
+ if fatal:
+ raise DistutilsError(msg % url)
+ else:
+ self.warn(msg, url)
+
+
+
+ def process_index(self,url,page):
+ """Process the contents of a PyPI page"""
+ def scan(link):
+ # Process a URL to see if it's for a package page
+ if link.startswith(self.index_url):
+ parts = map(
+ urllib2.unquote, link[len(self.index_url):].split('/')
+ )
+ if len(parts)==2:
+ # it's a package page, sanitize and index it
+ pkg = safe_name(parts[0])
+ ver = safe_version(parts[1])
+ self.package_pages.setdefault(pkg.lower(),{})[link] = True
+ return to_filename(pkg), to_filename(ver)
+ return None, None
+
+ if url==self.index_url or 'Index of Packages</title>' in page:
+ # process an index page into the package-page index
+ for match in HREF.finditer(page):
+ scan( urlparse.urljoin(url, match.group(1)) )
+ else:
+ pkg,ver = scan(url) # ensure this page is in the page index
+ # process individual package page
+ for tag in ("<th>Home Page", "<th>Download URL"):
+ pos = page.find(tag)
+ if pos!=-1:
+ match = HREF.search(page,pos)
+ if match:
+ # Process the found URL
+ new_url = urlparse.urljoin(url, match.group(1))
+ base, frag = egg_info_for_url(new_url)
+ if base.endswith('.py') and not frag:
+ if pkg and ver:
+ new_url+='#egg=%s-%s' % (pkg,ver)
+ else:
+ self.need_version_info(url)
+ self.scan_url(new_url)
+ return PYPI_MD5.sub(
+ lambda m: '<a href="%s#md5=%s">%s</a>' % m.group(1,3,2), page
+ )
+
+ def need_version_info(self, url):
+ self.scan_all(
+ "Page at %s links to .py file(s) without version info; an index "
+ "scan is required.", url
+ )
+
+ def scan_all(self, msg=None, *args):
+ if self.index_url not in self.fetched_urls:
+ if msg: self.warn(msg,*args)
+ self.warn(
+ "Scanning index of all packages (this may take a while)"
+ )
+ self.scan_url(self.index_url)
+
+ def find_packages(self, requirement):
+ self.scan_url(self.index_url + requirement.unsafe_name+'/')
+
+ if not self.package_pages.get(requirement.key):
+ # Fall back to safe version of the name
+ self.scan_url(self.index_url + requirement.project_name+'/')
+
+ if not self.package_pages.get(requirement.key):
+ # We couldn't find the target package, so search the index page too
+ self.warn(
+ "Couldn't find index page for %r (maybe misspelled?)",
+ requirement.unsafe_name
+ )
+ self.scan_all()
+
+ for url in self.package_pages.get(requirement.key,()):
+ # scan each page that might be related to the desired package
+ self.scan_url(url)
+
+ def obtain(self, requirement, installer=None):
+ self.prescan(); self.find_packages(requirement)
+ for dist in self[requirement.key]:
+ if dist in requirement:
+ return dist
+ self.debug("%s does not match %s", requirement, dist)
+ return super(PackageIndex, self).obtain(requirement,installer)
+
+ 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:]:
+ tfp.close()
+ os.unlink(filename)
+ raise DistutilsError(
+ "MD5 validation failed for "+os.path.basename(filename)+
+ "; possible download problem?"
+ )
+
+ def add_find_links(self, urls):
+ """Add `urls` to the list that will be prescanned for searches"""
+ for url in urls:
+ if (
+ self.to_scan is None # if we have already "gone online"
+ or not URL_SCHEME(url) # or it's a local file/directory
+ or url.startswith('file:')
+ or list(distros_for_url(url)) # or a direct package link
+ ):
+ # then go ahead and process it now
+ self.scan_url(url)
+ else:
+ # otherwise, defer retrieval till later
+ self.to_scan.append(url)
+
+ def prescan(self):
+ """Scan urls scheduled for prescanning (e.g. --find-links)"""
+ if self.to_scan:
+ map(self.scan_url, self.to_scan)
+ self.to_scan = None # from now on, go ahead and process immediately
+
+
+
+
+
+
+
+
+
+
+ def download(self, spec, tmpdir):
+ """Locate and/or download `spec` to `tmpdir`, returning a local path
+
+ `spec` may be a ``Requirement`` object, or a string containing a URL,
+ an existing local filename, or a project/version requirement spec
+ (i.e. the string form of a ``Requirement`` object). If it is the URL
+ of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one
+ that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is
+ automatically created alongside the downloaded file.
+
+ If `spec` is a ``Requirement`` object or a string containing a
+ project/version requirement spec, this method returns the location of
+ a matching distribution (possibly after downloading it to `tmpdir`).
+ If `spec` is a locally existing file or directory name, it is simply
+ returned unchanged. If `spec` is a URL, it is downloaded to a subpath
+ of `tmpdir`, and the local filename is returned. Various errors may be
+ raised if a problem occurs during downloading.
+ """
+ if not isinstance(spec,Requirement):
+ scheme = URL_SCHEME(spec)
+ if scheme:
+ # It's a url, download it to tmpdir
+ found = self._download_url(scheme.group(1), spec, tmpdir)
+ base, fragment = egg_info_for_url(spec)
+ if base.endswith('.py'):
+ found = self.gen_setup(found,fragment,tmpdir)
+ return found
+ elif os.path.exists(spec):
+ # Existing file or directory, just return it
+ return spec
+ else:
+ try:
+ spec = Requirement.parse(spec)
+ except ValueError:
+ raise DistutilsError(
+ "Not a URL, existing file, or requirement spec: %r" %
+ (spec,)
+ )
+ return getattr(self.fetch_distribution(spec, tmpdir),'location',None)
+
+
+ def fetch_distribution(self,
+ requirement, tmpdir, force_scan=False, source=False, develop_ok=False
+ ):
+ """Obtain a distribution suitable for fulfilling `requirement`
+
+ `requirement` must be a ``pkg_resources.Requirement`` instance.
+ If necessary, or if the `force_scan` flag is set, the requirement is
+ searched for in the (online) package index as well as the locally
+ installed packages. If a distribution matching `requirement` is found,
+ the returned distribution's ``location`` is the value you would have
+ gotten from calling the ``download()`` method with the matching
+ distribution's URL or filename. If no matching distribution is found,
+ ``None`` is returned.
+
+ If the `source` flag is set, only source distributions and source
+ checkout links will be considered. Unless the `develop_ok` flag is
+ 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 = {}
+
+ def find(req):
+ # Find a matching distribution; may be called more than once
+
+ for dist in self[req.key]:
+
+ if dist.precedence==DEVELOP_DIST and not develop_ok:
+ if dist not in skipped:
+ self.warn("Skipping development or system egg: %s",dist)
+ skipped[dist] = 1
+ 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)
+ )
+
+ if force_scan:
+ self.prescan()
+ self.find_packages(requirement)
+
+ dist = find(requirement)
+ if dist is None and self.to_scan is not None:
+ self.prescan()
+ dist = find(requirement)
+
+ if dist is None and not force_scan:
+ self.find_packages(requirement)
+ dist = find(requirement)
+
+ if dist is None:
+ self.warn(
+ "No local packages or download links found for %s%s",
+ (source and "a source distribution of " or ""),
+ requirement,
+ )
+ return dist
+
+ def fetch(self, requirement, tmpdir, force_scan=False, source=False):
+ """Obtain a file suitable for fulfilling `requirement`
+
+ DEPRECATED; use the ``fetch_distribution()`` method now instead. For
+ backward compatibility, this routine is identical but returns the
+ ``location`` of the downloaded distribution instead of a distribution
+ object.
+ """
+ dist = self.fetch_distribution(requirement,tmpdir,force_scan,source)
+ if dist is not None:
+ return dist.location
+ return None
+
+
+
+
+
+
+
+
+ def gen_setup(self, filename, fragment, tmpdir):
+ match = EGG_FRAGMENT.match(fragment); #import pdb; pdb.set_trace()
+ dists = match and [d for d in
+ interpret_distro_name(filename, match.group(1), None) if d.version
+ ] or []
+
+ if len(dists)==1: # unambiguous ``#egg`` fragment
+ basename = os.path.basename(filename)
+
+ # Make sure the file has been downloaded to the temp dir.
+ if os.path.dirname(filename) != tmpdir:
+ dst = os.path.join(tmpdir, basename)
+ from setuptools.command.easy_install import samefile
+ if not samefile(filename, dst):
+ shutil.copy2(filename, dst)
+ filename=dst
+
+ file = open(os.path.join(tmpdir, 'setup.py'), 'w')
+ file.write(
+ "from setuptools import setup\n"
+ "setup(name=%r, version=%r, py_modules=[%r])\n"
+ % (
+ dists[0].project_name, dists[0].version,
+ os.path.splitext(basename)[0]
+ )
+ )
+ file.close()
+ return filename
+
+ elif match:
+ raise DistutilsError(
+ "Can't unambiguously interpret project/version identifier %r; "
+ "any dashes in the name or version should be escaped using "
+ "underscores. %r" % (fragment,dists)
+ )
+ else:
+ raise DistutilsError(
+ "Can't process plain .py files without an '#egg=name-version'"
+ " suffix to enable automatic setup script generation."
+ )
+
+ dl_blocksize = 8192
+ def _download_to(self, url, filename):
+ self.url_ok(url,True) # raises error if not allowed
+ self.info("Downloading %s", url)
+ # Download the file
+ fp, tfp, info = None, None, None
+ try:
+ if '#' in url:
+ url, info = url.split('#', 1)
+ fp = self.open_url(url)
+ if isinstance(fp, urllib2.HTTPError):
+ raise DistutilsError(
+ "Can't download %s: %s %s" % (url, fp.code,fp.msg)
+ )
+ cs = md5()
+ headers = fp.info()
+ blocknum = 0
+ bs = self.dl_blocksize
+ size = -1
+ if "content-length" in headers:
+ size = int(headers["Content-Length"])
+ self.reporthook(url, filename, blocknum, bs, size)
+ tfp = open(filename,'wb')
+ while True:
+ block = fp.read(bs)
+ if block:
+ cs.update(block)
+ tfp.write(block)
+ blocknum += 1
+ self.reporthook(url, filename, blocknum, bs, size)
+ else:
+ break
+ if info: self.check_md5(cs, info, filename, tfp)
+ return headers
+ finally:
+ if fp: fp.close()
+ if tfp: tfp.close()
+
+ def reporthook(self, url, filename, blocknum, blksize, size):
+ pass # no-op
+
+ def retry_sf_download(self, url, filename):
+ try:
+ return self._download_to(url, filename)
+ except:
+ scheme, server, path, param, query, frag = urlparse.urlparse(url)
+ if server!='dl.sourceforge.net':
+ raise
+
+ mirror = get_sf_ip()
+
+ while _sf_mirrors:
+ self.warn("Download failed: %s", sys.exc_info()[1])
+ url = urlparse.urlunparse((scheme, mirror, path, param, '', frag))
+ try:
+ return self._download_to(url, filename)
+ except:
+ _sf_mirrors.remove(mirror) # don't retry the same mirror
+ mirror = get_sf_ip()
+
+ raise # fail if no mirror works
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ def open_url(self, url):
+ try:
+ return urllib2.urlopen(url)
+ except urllib2.HTTPError, v:
+ return v
+ except urllib2.URLError, v:
+ raise DistutilsError("Download error: %s" % v.reason)
+
+
+ def _download_url(self, scheme, url, tmpdir):
+
+ # Determine download filename
+ #
+ name = filter(None,urlparse.urlparse(url)[2].split('/'))
+ if name:
+ name = name[-1]
+ while '..' in name:
+ name = name.replace('..','.').replace('\\','_')
+ else:
+ name = "__downloaded__" # default if URL has no path contents
+
+ if name.endswith('.egg.zip'):
+ name = name[:-4] # strip the extra .zip before download
+
+ filename = os.path.join(tmpdir,name)
+
+ # Download the file
+ #
+ if scheme=='svn' or scheme.startswith('svn+'):
+ return self._download_svn(url, filename)
+ else:
+ headers = self.retry_sf_download(url, filename)
+ if 'html' in headers['content-type'].lower():
+ return self._download_html(url, headers, filename, tmpdir)
+ else:
+ return filename
+
+ def scan_url(self, url):
+ self.process_url(url, True)
+
+
+ def _download_html(self, url, headers, filename, tmpdir):
+ file = open(filename)
+ for line in file:
+ if line.strip():
+ # Check for a subversion index page
+ if re.search(r'<title>Revision \d+:', line):
+ # it's a subversion index page:
+ file.close()
+ os.unlink(filename)
+ return self._download_svn(url, filename)
+ break # not an index page
+ file.close()
+ os.unlink(filename)
+ raise DistutilsError("Unexpected HTML page found at "+url)
+
+ def _download_svn(self, url, filename):
+ url = url.split('#',1)[0] # remove any fragment for svn's sake
+ self.info("Doing subversion checkout from %s to %s", url, filename)
+ os.system("svn checkout -q %s %s" % (url, filename))
+ return filename
+
+ def debug(self, msg, *args):
+ log.debug(msg, *args)
+
+ def info(self, msg, *args):
+ log.info(msg, *args)
+
+ def warn(self, msg, *args):
+ log.warn(msg, *args)
+
+
+
+
+
+
+
+
+
+
+
+
+def fix_sf_url(url):
+ scheme, server, path, param, query, frag = urlparse.urlparse(url)
+ if server!='prdownloads.sourceforge.net':
+ return url
+ return urlparse.urlunparse(
+ (scheme, 'dl.sourceforge.net', 'sourceforge'+path, param, '', frag)
+ )
+
+_sf_mirrors = []
+
+def get_sf_ip():
+ if not _sf_mirrors:
+ try:
+ _sf_mirrors[:] = socket.gethostbyname_ex('dl.sourceforge.net')[-1]
+ except socket.error:
+ # DNS-bl0ck1n9 f1r3w4llz sUx0rs!
+ _sf_mirrors[:] = ['dl.sourceforge.net']
+ return random.choice(_sf_mirrors)
diff --git a/Lib/setuptools/sandbox.py b/Lib/setuptools/sandbox.py
new file mode 100755
index 0000000..606944b
--- /dev/null
+++ b/Lib/setuptools/sandbox.py
@@ -0,0 +1,203 @@
+import os, sys, __builtin__, tempfile
+_os = sys.modules[os.name]
+_open = open
+from distutils.errors import DistutilsError
+__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[:]
+ setup_dir = os.path.abspath(os.path.dirname(setup_script))
+ temp_dir = os.path.join(setup_dir,'temp')
+ if not os.path.isdir(temp_dir): os.makedirs(temp_dir)
+ save_tmp = tempfile.tempdir
+
+ try:
+ tempfile.tempdir = temp_dir
+ os.chdir(setup_dir)
+ try:
+ sys.argv[:] = [setup_script]+list(args)
+ sys.path.insert(0, setup_dir)
+ DirectorySandbox(setup_dir).run(
+ lambda: execfile(
+ "setup.py",
+ {'__file__':setup_script, '__name__':'__main__'}
+ )
+ )
+ except SystemExit, v:
+ if v.args and v.args[0]:
+ raise
+ # Normal exit, just return
+ finally:
+ 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"""
+
+ _active = False
+
+ def __init__(self):
+ self._attrs = [
+ name for name in dir(_os)
+ if not name.startswith('_') and hasattr(self,name)
+ ]
+
+ def _copy(self, source):
+ for name in self._attrs:
+ setattr(os, name, getattr(source,name))
+
+ def run(self, func):
+ """Run 'func' under os sandboxing"""
+ try:
+ self._copy(self)
+ __builtin__.open = __builtin__.file = self._open
+ self._active = True
+ return func()
+ finally:
+ self._active = False
+ __builtin__.open = __builtin__.file = _open
+ self._copy(_os)
+
+
+ def _mk_dual_path_wrapper(name):
+ original = getattr(_os,name)
+ def wrap(self,src,dst,*args,**kw):
+ if self._active:
+ src,dst = self._remap_pair(name,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)
+
+
+ def _mk_single_path_wrapper(name, original=None):
+ original = original or getattr(_os,name)
+ def wrap(self,path,*args,**kw):
+ if self._active:
+ path = self._remap_input(name,path,*args,**kw)
+ return original(path,*args,**kw)
+ return wrap
+
+ _open = _mk_single_path_wrapper('file', _open)
+ for name in [
+ "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir",
+ "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat",
+ "startfile", "mkfifo", "mknod", "pathconf", "access"
+ ]:
+ 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):
+ if self._active:
+ path = self._remap_input(name,path,*args,**kw)
+ return self._remap_output(name, original(path,*args,**kw))
+ return original(path,*args,**kw)
+ return wrap
+
+ for name in ['readlink', 'tempnam']:
+ if hasattr(_os,name): locals()[name] = _mk_single_with_return(name)
+
+ def _mk_query(name):
+ original = getattr(_os,name)
+ def wrap(self,*args,**kw):
+ retval = original(*args,**kw)
+ if self._active:
+ return self._remap_output(name, retval)
+ return retval
+ return wrap
+
+ for name in ['getcwd', 'tmpnam']:
+ if hasattr(_os,name): locals()[name] = _mk_query(name)
+
+ def _validate_path(self,path):
+ """Called to remap or validate any path, whether input or output"""
+ return path
+
+ def _remap_input(self,operation,path,*args,**kw):
+ """Called for path inputs"""
+ return self._validate_path(path)
+
+ def _remap_output(self,operation,path):
+ """Called for path outputs"""
+ return self._validate_path(path)
+
+ def _remap_pair(self,operation,src,dst,*args,**kw):
+ """Called for path pairs like rename, link, and symlink operations"""
+ return (
+ self._remap_input(operation+'-from',src,*args,**kw),
+ self._remap_input(operation+'-to',dst,*args,**kw)
+ )
+
+
+class DirectorySandbox(AbstractSandbox):
+ """Restrict operations to a single subdirectory - pseudo-chroot"""
+
+ write_ops = dict.fromkeys([
+ "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir",
+ "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam",
+ ])
+
+ def __init__(self,sandbox):
+ self._sandbox = os.path.normcase(os.path.realpath(sandbox))
+ self._prefix = os.path.join(self._sandbox,'')
+ AbstractSandbox.__init__(self)
+
+ def _violation(self, operation, *args, **kw):
+ raise SandboxViolation(operation, args, kw)
+
+ def _open(self, path, mode='r', *args, **kw):
+ if mode not in ('r', 'rt', 'rb', 'rU') and not self._ok(path):
+ self._violation("open", path, mode, *args, **kw)
+ return _open(path,mode,*args,**kw)
+
+ def tmpnam(self):
+ self._violation("tmpnam")
+
+ def _ok(self,path):
+ active = self._active
+ try:
+ self._active = False
+ realpath = os.path.normcase(os.path.realpath(path))
+ if realpath==self._sandbox or realpath.startswith(self._prefix):
+ return True
+ finally:
+ self._active = active
+
+ def _remap_input(self,operation,path,*args,**kw):
+ """Called for path inputs"""
+ if operation in self.write_ops and not self._ok(path):
+ self._violation(operation, os.path.realpath(path), *args, **kw)
+ return path
+
+ def _remap_pair(self,operation,src,dst,*args,**kw):
+ """Called for path pairs like rename, link, and symlink operations"""
+ if not self._ok(src) or not self._ok(dst):
+ self._violation(operation, src, dst, *args, **kw)
+ return (src,dst)
+
+
+class SandboxViolation(DistutilsError):
+ """A setup script attempted to modify the filesystem outside the sandbox"""
+
+ def __str__(self):
+ return """SandboxViolation: %s%r %s
+
+The package setup script has attempted to modify files on your system
+that are not within the EasyInstall build area, and has been aborted.
+
+This package cannot be safely installed by EasyInstall, and may not
+support alternate installation locations even if you run its setup
+script by hand. Please inform the package's author and the EasyInstall
+maintainers to find out if a fix or workaround is available.""" % self.args
diff --git a/Lib/setuptools/site-patch.py b/Lib/setuptools/site-patch.py
new file mode 100755
index 0000000..b1b27b9
--- /dev/null
+++ b/Lib/setuptools/site-patch.py
@@ -0,0 +1,74 @@
+def __boot():
+ import sys, imp, os, os.path
+ PYTHONPATH = os.environ.get('PYTHONPATH')
+ if PYTHONPATH is None or (sys.platform=='win32' and not PYTHONPATH):
+ PYTHONPATH = []
+ else:
+ PYTHONPATH = PYTHONPATH.split(os.pathsep)
+
+ pic = getattr(sys,'path_importer_cache',{})
+ stdpath = sys.path[len(PYTHONPATH):]
+ mydir = os.path.dirname(__file__)
+ #print "searching",stdpath,sys.path
+
+ for item in stdpath:
+ if item==mydir or not item:
+ continue # skip if current dir. on Windows, or my own directory
+ importer = pic.get(item)
+ if importer is not None:
+ loader = importer.find_module('site')
+ if loader is not None:
+ # This should actually reload the current module
+ loader.load_module('site')
+ break
+ else:
+ try:
+ stream, path, descr = imp.find_module('site',[item])
+ except ImportError:
+ continue
+ if stream is None:
+ continue
+ try:
+ # This should actually reload the current module
+ imp.load_module('site',stream,path,descr)
+ finally:
+ stream.close()
+ break
+ else:
+ raise ImportError("Couldn't find the real 'site' module")
+
+ #print "loaded", __file__
+
+ known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp
+
+ oldpos = getattr(sys,'__egginsert',0) # save old insertion position
+ sys.__egginsert = 0 # and reset the current one
+
+ for item in PYTHONPATH:
+ addsitedir(item)
+
+ sys.__egginsert += oldpos # restore effective old position
+
+ d,nd = makepath(stdpath[0])
+ insert_at = None
+ new_path = []
+
+ for item in sys.path:
+ p,np = makepath(item)
+
+ if np==nd and insert_at is None:
+ # We've hit the first 'system' path entry, so added entries go here
+ insert_at = len(new_path)
+
+ if np in known_paths or insert_at is None:
+ new_path.append(item)
+ else:
+ # new path after the insert point, back-insert it
+ new_path.insert(insert_at, item)
+ insert_at += 1
+
+ sys.path[:] = new_path
+
+if __name__=='site':
+ __boot()
+ del __boot
diff --git a/Lib/setuptools/tests/__init__.py b/Lib/setuptools/tests/__init__.py
new file mode 100644
index 0000000..8a767dc
--- /dev/null
+++ b/Lib/setuptools/tests/__init__.py
@@ -0,0 +1,364 @@
+"""Tests for the 'setuptools' package"""
+
+from unittest import TestSuite, TestCase, makeSuite, defaultTestLoader
+import distutils.core, distutils.cmd
+from distutils.errors import DistutilsOptionError, DistutilsPlatformError
+from distutils.errors import DistutilsSetupError
+import setuptools, setuptools.dist
+from setuptools import Feature
+from distutils.core import Extension
+from setuptools.depends import extract_constant, get_module_constant
+from setuptools.depends import find_module, Require
+from distutils.version import StrictVersion, LooseVersion
+from distutils.util import convert_path
+import sys, os.path
+
+def additional_tests():
+ import doctest
+ return doctest.DocFileSuite(
+ 'api_tests.txt', optionflags=doctest.ELLIPSIS, package=__name__,
+ )
+
+
+def makeSetup(**args):
+ """Return distribution from 'setup(**args)', without executing commands"""
+
+ distutils.core._setup_stop_after = "commandline"
+
+ # Don't let system command line leak into tests!
+ args.setdefault('script_args',['install'])
+
+ try:
+ return setuptools.setup(**args)
+ finally:
+ distutils.core_setup_stop_after = None
+
+
+
+
+
+
+
+class DependsTests(TestCase):
+
+ def testExtractConst(self):
+
+ from setuptools.depends import extract_constant
+
+ def f1():
+ global x,y,z
+ x = "test"
+ y = z
+
+ # unrecognized name
+ self.assertEqual(extract_constant(f1.func_code,'q', -1), None)
+
+ # constant assigned
+ self.assertEqual(extract_constant(f1.func_code,'x', -1), "test")
+
+ # expression assigned
+ self.assertEqual(extract_constant(f1.func_code,'y', -1), -1)
+
+ # recognized name, not assigned
+ self.assertEqual(extract_constant(f1.func_code,'z', -1), None)
+
+
+ def testFindModule(self):
+ self.assertRaises(ImportError, find_module, 'no-such.-thing')
+ self.assertRaises(ImportError, find_module, 'setuptools.non-existent')
+ f,p,i = find_module('setuptools.tests'); f.close()
+
+ def testModuleExtract(self):
+ from distutils import __version__
+ self.assertEqual(
+ get_module_constant('distutils','__version__'), __version__
+ )
+ self.assertEqual(
+ get_module_constant('sys','version'), sys.version
+ )
+ self.assertEqual(
+ get_module_constant('setuptools.tests','__doc__'),__doc__
+ )
+
+ def testRequire(self):
+
+ req = Require('Distutils','1.0.3','distutils')
+
+ self.assertEqual(req.name, 'Distutils')
+ self.assertEqual(req.module, 'distutils')
+ self.assertEqual(req.requested_version, '1.0.3')
+ self.assertEqual(req.attribute, '__version__')
+ self.assertEqual(req.full_name(), 'Distutils-1.0.3')
+
+ from distutils import __version__
+ self.assertEqual(req.get_version(), __version__)
+ self.failUnless(req.version_ok('1.0.9'))
+ self.failIf(req.version_ok('0.9.1'))
+ self.failIf(req.version_ok('unknown'))
+
+ self.failUnless(req.is_present())
+ self.failUnless(req.is_current())
+
+ req = Require('Distutils 3000','03000','distutils',format=LooseVersion)
+ self.failUnless(req.is_present())
+ self.failIf(req.is_current())
+ self.failIf(req.version_ok('unknown'))
+
+ req = Require('Do-what-I-mean','1.0','d-w-i-m')
+ self.failIf(req.is_present())
+ self.failIf(req.is_current())
+
+ req = Require('Tests', None, 'tests', homepage="http://example.com")
+ self.assertEqual(req.format, None)
+ self.assertEqual(req.attribute, None)
+ self.assertEqual(req.requested_version, None)
+ self.assertEqual(req.full_name(), 'Tests')
+ self.assertEqual(req.homepage, 'http://example.com')
+
+ paths = [os.path.dirname(p) for p in __path__]
+ self.failUnless(req.is_present(paths))
+ self.failUnless(req.is_current(paths))
+
+
+
+class DistroTests(TestCase):
+
+ def setUp(self):
+ self.e1 = Extension('bar.ext',['bar.c'])
+ self.e2 = Extension('c.y', ['y.c'])
+
+ self.dist = makeSetup(
+ packages=['a', 'a.b', 'a.b.c', 'b', 'c'],
+ py_modules=['b.d','x'],
+ ext_modules = (self.e1, self.e2),
+ package_dir = {},
+ )
+
+
+ def testDistroType(self):
+ self.failUnless(isinstance(self.dist,setuptools.dist.Distribution))
+
+
+ def testExcludePackage(self):
+ self.dist.exclude_package('a')
+ self.assertEqual(self.dist.packages, ['b','c'])
+
+ self.dist.exclude_package('b')
+ self.assertEqual(self.dist.packages, ['c'])
+ self.assertEqual(self.dist.py_modules, ['x'])
+ self.assertEqual(self.dist.ext_modules, [self.e1, self.e2])
+
+ self.dist.exclude_package('c')
+ self.assertEqual(self.dist.packages, [])
+ self.assertEqual(self.dist.py_modules, ['x'])
+ self.assertEqual(self.dist.ext_modules, [self.e1])
+
+ # test removals from unspecified options
+ makeSetup().exclude_package('x')
+
+
+
+
+
+
+
+ def testIncludeExclude(self):
+ # remove an extension
+ self.dist.exclude(ext_modules=[self.e1])
+ self.assertEqual(self.dist.ext_modules, [self.e2])
+
+ # add it back in
+ self.dist.include(ext_modules=[self.e1])
+ self.assertEqual(self.dist.ext_modules, [self.e2, self.e1])
+
+ # should not add duplicate
+ self.dist.include(ext_modules=[self.e1])
+ self.assertEqual(self.dist.ext_modules, [self.e2, self.e1])
+
+ def testExcludePackages(self):
+ self.dist.exclude(packages=['c','b','a'])
+ self.assertEqual(self.dist.packages, [])
+ self.assertEqual(self.dist.py_modules, ['x'])
+ self.assertEqual(self.dist.ext_modules, [self.e1])
+
+ def testEmpty(self):
+ dist = makeSetup()
+ dist.include(packages=['a'], py_modules=['b'], ext_modules=[self.e2])
+ dist = makeSetup()
+ dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2])
+
+ def testContents(self):
+ self.failUnless(self.dist.has_contents_for('a'))
+ self.dist.exclude_package('a')
+ self.failIf(self.dist.has_contents_for('a'))
+
+ self.failUnless(self.dist.has_contents_for('b'))
+ self.dist.exclude_package('b')
+ self.failIf(self.dist.has_contents_for('b'))
+
+ self.failUnless(self.dist.has_contents_for('c'))
+ self.dist.exclude_package('c')
+ self.failIf(self.dist.has_contents_for('c'))
+
+
+
+
+ def testInvalidIncludeExclude(self):
+ self.assertRaises(DistutilsSetupError,
+ self.dist.include, nonexistent_option='x'
+ )
+ self.assertRaises(DistutilsSetupError,
+ self.dist.exclude, nonexistent_option='x'
+ )
+ self.assertRaises(DistutilsSetupError,
+ self.dist.include, packages={'x':'y'}
+ )
+ self.assertRaises(DistutilsSetupError,
+ self.dist.exclude, packages={'x':'y'}
+ )
+ self.assertRaises(DistutilsSetupError,
+ self.dist.include, ext_modules={'x':'y'}
+ )
+ self.assertRaises(DistutilsSetupError,
+ self.dist.exclude, ext_modules={'x':'y'}
+ )
+
+ self.assertRaises(DistutilsSetupError,
+ self.dist.include, package_dir=['q']
+ )
+ self.assertRaises(DistutilsSetupError,
+ self.dist.exclude, package_dir=['q']
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class FeatureTests(TestCase):
+
+ def setUp(self):
+ self.req = Require('Distutils','1.0.3','distutils')
+ self.dist = makeSetup(
+ features={
+ 'foo': Feature("foo",standard=True,require_features=['baz',self.req]),
+ 'bar': Feature("bar", standard=True, packages=['pkg.bar'],
+ py_modules=['bar_et'], remove=['bar.ext'],
+ ),
+ 'baz': Feature(
+ "baz", optional=False, packages=['pkg.baz'],
+ scripts = ['scripts/baz_it'],
+ libraries=[('libfoo','foo/foofoo.c')]
+ ),
+ 'dwim': Feature("DWIM", available=False, remove='bazish'),
+ },
+ script_args=['--without-bar', 'install'],
+ packages = ['pkg.bar', 'pkg.foo'],
+ py_modules = ['bar_et', 'bazish'],
+ ext_modules = [Extension('bar.ext',['bar.c'])]
+ )
+
+ def testDefaults(self):
+ self.failIf(
+ Feature(
+ "test",standard=True,remove='x',available=False
+ ).include_by_default()
+ )
+ self.failUnless(
+ Feature("test",standard=True,remove='x').include_by_default()
+ )
+ # Feature must have either kwargs, removes, or require_features
+ self.assertRaises(DistutilsSetupError, Feature, "test")
+
+ def testAvailability(self):
+ self.assertRaises(
+ DistutilsPlatformError,
+ self.dist.features['dwim'].include_in, self.dist
+ )
+
+ def testFeatureOptions(self):
+ dist = self.dist
+ self.failUnless(
+ ('with-dwim',None,'include DWIM') in dist.feature_options
+ )
+ self.failUnless(
+ ('without-dwim',None,'exclude DWIM (default)') in dist.feature_options
+ )
+ self.failUnless(
+ ('with-bar',None,'include bar (default)') in dist.feature_options
+ )
+ self.failUnless(
+ ('without-bar',None,'exclude bar') in dist.feature_options
+ )
+ self.assertEqual(dist.feature_negopt['without-foo'],'with-foo')
+ self.assertEqual(dist.feature_negopt['without-bar'],'with-bar')
+ self.assertEqual(dist.feature_negopt['without-dwim'],'with-dwim')
+ self.failIf('without-baz' in dist.feature_negopt)
+
+ def testUseFeatures(self):
+ dist = self.dist
+ self.assertEqual(dist.with_foo,1)
+ self.assertEqual(dist.with_bar,0)
+ self.assertEqual(dist.with_baz,1)
+ self.failIf('bar_et' in dist.py_modules)
+ self.failIf('pkg.bar' in dist.packages)
+ self.failUnless('pkg.baz' in dist.packages)
+ self.failUnless('scripts/baz_it' in dist.scripts)
+ self.failUnless(('libfoo','foo/foofoo.c') in dist.libraries)
+ self.assertEqual(dist.ext_modules,[])
+ self.assertEqual(dist.require_features, [self.req])
+
+ # If we ask for bar, it should fail because we explicitly disabled
+ # it on the command line
+ self.assertRaises(DistutilsOptionError, dist.include_feature, 'bar')
+
+ def testFeatureWithInvalidRemove(self):
+ self.assertRaises(
+ SystemExit, makeSetup, features = {'x':Feature('x', remove='y')}
+ )
+
+class TestCommandTests(TestCase):
+
+ def testTestIsCommand(self):
+ test_cmd = makeSetup().get_command_obj('test')
+ self.failUnless(isinstance(test_cmd, distutils.cmd.Command))
+
+ def testLongOptSuiteWNoDefault(self):
+ ts1 = makeSetup(script_args=['test','--test-suite=foo.tests.suite'])
+ ts1 = ts1.get_command_obj('test')
+ ts1.ensure_finalized()
+ self.assertEqual(ts1.test_suite, 'foo.tests.suite')
+
+ def testDefaultSuite(self):
+ ts2 = makeSetup(test_suite='bar.tests.suite').get_command_obj('test')
+ ts2.ensure_finalized()
+ self.assertEqual(ts2.test_suite, 'bar.tests.suite')
+
+ def testDefaultWModuleOnCmdLine(self):
+ ts3 = makeSetup(
+ test_suite='bar.tests',
+ script_args=['test','-m','foo.tests']
+ ).get_command_obj('test')
+ ts3.ensure_finalized()
+ self.assertEqual(ts3.test_module, 'foo.tests')
+ self.assertEqual(ts3.test_suite, 'foo.tests.test_suite')
+
+ def testConflictingOptions(self):
+ ts4 = makeSetup(
+ script_args=['test','-m','bar.tests', '-s','foo.tests.suite']
+ ).get_command_obj('test')
+ self.assertRaises(DistutilsOptionError, ts4.ensure_finalized)
+
+ def testNoSuite(self):
+ ts5 = makeSetup().get_command_obj('test')
+ ts5.ensure_finalized()
+ self.assertEqual(ts5.test_suite, None)
diff --git a/Lib/setuptools/tests/api_tests.txt b/Lib/setuptools/tests/api_tests.txt
new file mode 100755
index 0000000..735ad8d
--- /dev/null
+++ b/Lib/setuptools/tests/api_tests.txt
@@ -0,0 +1,330 @@
+Pluggable Distributions of Python Software
+==========================================
+
+Distributions
+-------------
+
+A "Distribution" is a collection of files that represent a "Release" of a
+"Project" as of a particular point in time, denoted by a
+"Version"::
+
+ >>> import sys, pkg_resources
+ >>> from pkg_resources import Distribution
+ >>> Distribution(project_name="Foo", version="1.2")
+ Foo 1.2
+
+Distributions have a location, which can be a filename, URL, or really anything
+else you care to use::
+
+ >>> dist = Distribution(
+ ... location="http://example.com/something",
+ ... project_name="Bar", version="0.9"
+ ... )
+
+ >>> dist
+ Bar 0.9 (http://example.com/something)
+
+
+Distributions have various introspectable attributes::
+
+ >>> dist.location
+ 'http://example.com/something'
+
+ >>> dist.project_name
+ 'Bar'
+
+ >>> dist.version
+ '0.9'
+
+ >>> dist.py_version == sys.version[:3]
+ True
+
+ >>> print dist.platform
+ None
+
+Including various computed attributes::
+
+ >>> from pkg_resources import parse_version
+ >>> dist.parsed_version == parse_version(dist.version)
+ True
+
+ >>> dist.key # case-insensitive form of the project name
+ 'bar'
+
+Distributions are compared (and hashed) by version first::
+
+ >>> Distribution(version='1.0') == Distribution(version='1.0')
+ True
+ >>> Distribution(version='1.0') == Distribution(version='1.1')
+ False
+ >>> Distribution(version='1.0') < Distribution(version='1.1')
+ True
+
+but also by project name (case-insensitive), platform, Python version,
+location, etc.::
+
+ >>> Distribution(project_name="Foo",version="1.0") == \
+ ... Distribution(project_name="Foo",version="1.0")
+ True
+
+ >>> Distribution(project_name="Foo",version="1.0") == \
+ ... Distribution(project_name="foo",version="1.0")
+ True
+
+ >>> Distribution(project_name="Foo",version="1.0") == \
+ ... Distribution(project_name="Foo",version="1.1")
+ False
+
+ >>> Distribution(project_name="Foo",py_version="2.3",version="1.0") == \
+ ... Distribution(project_name="Foo",py_version="2.4",version="1.0")
+ False
+
+ >>> Distribution(location="spam",version="1.0") == \
+ ... Distribution(location="spam",version="1.0")
+ True
+
+ >>> Distribution(location="spam",version="1.0") == \
+ ... Distribution(location="baz",version="1.0")
+ False
+
+
+
+Hash and compare distribution by prio/plat
+
+Get version from metadata
+provider capabilities
+egg_name()
+as_requirement()
+from_location, from_filename (w/path normalization)
+
+Releases may have zero or more "Requirements", which indicate
+what releases of another project the release requires in order to
+function. A Requirement names the other project, expresses some criteria
+as to what releases of that project are acceptable, and lists any "Extras"
+that the requiring release may need from that project. (An Extra is an
+optional feature of a Release, that can only be used if its additional
+Requirements are satisfied.)
+
+
+
+The Working Set
+---------------
+
+A collection of active distributions is called a Working Set. Note that a
+Working Set can contain any importable distribution, not just pluggable ones.
+For example, the Python standard library is an importable distribution that
+will usually be part of the Working Set, even though it is not pluggable.
+Similarly, when you are doing development work on a project, the files you are
+editing are also a Distribution. (And, with a little attention to the
+directory names used, and including some additional metadata, such a
+"development distribution" can be made pluggable as well.)
+
+ >>> from pkg_resources import WorkingSet
+
+A working set's entries are the sys.path entries that correspond to the active
+distributions. By default, the working set's entries are the items on
+``sys.path``::
+
+ >>> ws = WorkingSet()
+ >>> ws.entries == sys.path
+ True
+
+But you can also create an empty working set explicitly, and add distributions
+to it::
+
+ >>> ws = WorkingSet([])
+ >>> ws.add(dist)
+ >>> ws.entries
+ ['http://example.com/something']
+ >>> dist in ws
+ True
+ >>> Distribution('foo',version="") in ws
+ False
+
+And you can iterate over its distributions::
+
+ >>> list(ws)
+ [Bar 0.9 (http://example.com/something)]
+
+Adding the same distribution more than once is a no-op::
+
+ >>> ws.add(dist)
+ >>> list(ws)
+ [Bar 0.9 (http://example.com/something)]
+
+For that matter, adding multiple distributions for the same project also does
+nothing, because a working set can only hold one active distribution per
+project -- the first one added to it::
+
+ >>> ws.add(
+ ... Distribution(
+ ... 'http://example.com/something', project_name="Bar",
+ ... version="7.2"
+ ... )
+ ... )
+ >>> list(ws)
+ [Bar 0.9 (http://example.com/something)]
+
+You can append a path entry to a working set using ``add_entry()``::
+
+ >>> ws.entries
+ ['http://example.com/something']
+ >>> ws.add_entry(pkg_resources.__file__)
+ >>> ws.entries
+ ['http://example.com/something', '...pkg_resources.py...']
+
+Multiple additions result in multiple entries, even if the entry is already in
+the working set (because ``sys.path`` can contain the same entry more than
+once)::
+
+ >>> ws.add_entry(pkg_resources.__file__)
+ >>> ws.entries
+ ['...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()``::
+
+ >>> ws = WorkingSet([])
+ >>> ws.add(dist,"foo")
+ >>> ws.entries
+ ['foo']
+
+But even if a distribution is found under multiple path entries, it still only
+shows up once when iterating the working set:
+
+ >>> ws.add_entry(ws.entries[0])
+ >>> list(ws)
+ [Bar 0.9 (http://example.com/something)]
+
+You can ask a WorkingSet to ``find()`` a distribution matching a requirement::
+
+ >>> from pkg_resources import Requirement
+ >>> print ws.find(Requirement.parse("Foo==1.0")) # no match, return None
+ None
+
+ >>> ws.find(Requirement.parse("Bar==0.9")) # match, return distribution
+ Bar 0.9 (http://example.com/something)
+
+Note that asking for a conflicting version of a distribution already in a
+working set triggers a ``pkg_resources.VersionConflict`` error:
+
+ >>> ws.find(Requirement.parse("Bar==1.0")) # doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ VersionConflict: (Bar 0.9 (http://example.com/something),
+ Requirement.parse('Bar==1.0'))
+
+You can subscribe a callback function to receive notifications whenever a new
+distribution is added to a working set. The callback is immediately invoked
+once for each existing distribution in the working set, and then is called
+again for new distributions added thereafter::
+
+ >>> def added(dist): print "Added", dist
+ >>> ws.subscribe(added)
+ Added Bar 0.9
+ >>> 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", location="f14")
+ >>> ws.add(foo14) # no callback, because Foo 1.2 is already active
+
+ >>> ws = WorkingSet([])
+ >>> ws.add(foo12)
+ >>> ws.add(foo14)
+ >>> ws.subscribe(added)
+ Added Foo 1.2
+
+And adding a callback more than once has no effect, either::
+
+ >>> ws.subscribe(added) # no callbacks
+
+ # and no double-callbacks on subsequent additions, either
+ >>> just_a_test = Distribution(project_name="JustATest", version="0.99")
+ >>> ws.add(just_a_test)
+ Added JustATest 0.99
+
+
+Finding Plugins
+---------------
+
+``WorkingSet`` objects can be used to figure out what plugins in an
+``Environment`` can be loaded without any resolution errors::
+
+ >>> from pkg_resources import Environment
+
+ >>> plugins = Environment([]) # normally, a list of plugin directories
+ >>> plugins.add(foo12)
+ >>> plugins.add(foo14)
+ >>> plugins.add(just_a_test)
+
+In the simplest case, we just get the newest version of each distribution in
+the plugin environment::
+
+ >>> ws = WorkingSet([])
+ >>> ws.find_plugins(plugins)
+ ([JustATest 0.99, Foo 1.4 (f14)], {})
+
+But if there's a problem with a version conflict or missing requirements, the
+method falls back to older versions, and the error info dict will contain an
+exception instance for each unloadable plugin::
+
+ >>> ws.add(foo12) # this will conflict with Foo 1.4
+ >>> ws.find_plugins(plugins)
+ ([JustATest 0.99, Foo 1.2 (f12)], {Foo 1.4 (f14): VersionConflict(...)})
+
+But if you disallow fallbacks, the failed plugin will be skipped instead of
+trying older versions::
+
+ >>> ws.find_plugins(plugins, fallback=False)
+ ([JustATest 0.99], {Foo 1.4 (f14): VersionConflict(...)})
+
+
+
+Platform Compatibility Rules
+----------------------------
+
+On the Mac, there are potential compatibility issues for modules compiled
+on newer versions of Mac OS X than what the user is running. Additionally,
+Mac OS X will soon have two platforms to contend with: Intel and PowerPC.
+
+Basic equality works as on other platforms::
+
+ >>> from pkg_resources import compatible_platforms as cp
+ >>> reqd = 'macosx-10.4-ppc'
+ >>> cp(reqd, reqd)
+ True
+ >>> cp("win32", reqd)
+ False
+
+Distributions made on other machine types are not compatible::
+
+ >>> cp("macosx-10.4-i386", reqd)
+ False
+
+Distributions made on earlier versions of the OS are compatible, as
+long as they are from the same top-level version. The patchlevel version
+number does not matter::
+
+ >>> cp("macosx-10.4-ppc", reqd)
+ True
+ >>> cp("macosx-10.3-ppc", reqd)
+ True
+ >>> cp("macosx-10.5-ppc", reqd)
+ False
+ >>> cp("macosx-9.5-ppc", reqd)
+ False
+
+Backwards compatibility for packages made via earlier versions of
+setuptools is provided as well::
+
+ >>> cp("darwin-8.2.0-Power_Macintosh", reqd)
+ True
+ >>> cp("darwin-7.2.0-Power_Macintosh", reqd)
+ True
+ >>> cp("darwin-8.2.0-Power_Macintosh", "macosx-10.3-ppc")
+ False
+
diff --git a/Lib/setuptools/tests/test_resources.py b/Lib/setuptools/tests/test_resources.py
new file mode 100644
index 0000000..f32c72e
--- /dev/null
+++ b/Lib/setuptools/tests/test_resources.py
@@ -0,0 +1,483 @@
+from unittest import TestCase, makeSuite
+from pkg_resources import *
+import pkg_resources, sys
+from sets import ImmutableSet
+
+class Metadata(EmptyProvider):
+ """Mock object to return metadata as if from an on-disk distribution"""
+
+ def __init__(self,*pairs):
+ self.metadata = dict(pairs)
+
+ def has_metadata(self,name):
+ return name in self.metadata
+
+ def get_metadata(self,name):
+ return self.metadata[name]
+
+ def get_metadata_lines(self,name):
+ return yield_lines(self.get_metadata(name))
+
+
+class DistroTests(TestCase):
+
+ def testCollection(self):
+ # empty path should produce no distributions
+ ad = Environment([], platform=None, python=None)
+ self.assertEqual(list(ad), [])
+ self.assertEqual(ad['FooPkg'],[])
+
+ ad.add(Distribution.from_filename("FooPkg-1.3_1.egg"))
+ ad.add(Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg"))
+ ad.add(Distribution.from_filename("FooPkg-1.2-py2.4.egg"))
+
+ # Name is in there now
+ self.failUnless(ad['FooPkg'])
+
+ # But only 1 package
+ self.assertEqual(list(ad), ['foopkg'])
+
+
+
+ # Distributions sort by version
+ self.assertEqual(
+ [dist.version for dist in ad['FooPkg']], ['1.4','1.3-1','1.2']
+ )
+ # Removing a distribution leaves sequence alone
+ ad.remove(ad['FooPkg'][1])
+ self.assertEqual(
+ [dist.version for dist in ad['FooPkg']], ['1.4','1.2']
+ )
+ # And inserting adds them in order
+ ad.add(Distribution.from_filename("FooPkg-1.9.egg"))
+ self.assertEqual(
+ [dist.version for dist in ad['FooPkg']], ['1.9','1.4','1.2']
+ )
+
+ ws = WorkingSet([])
+ foo12 = Distribution.from_filename("FooPkg-1.2-py2.4.egg")
+ foo14 = Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg")
+ req, = parse_requirements("FooPkg>=1.3")
+
+ # Nominal case: no distros on path, should yield all applicable
+ self.assertEqual(ad.best_match(req,ws).version, '1.9')
+ # If a matching distro is already installed, should return only that
+ ws.add(foo14); self.assertEqual(ad.best_match(req,ws).version, '1.4')
+
+ # If the first matching distro is unsuitable, it's a version conflict
+ ws = WorkingSet([]); ws.add(foo12); ws.add(foo14)
+ self.assertRaises(VersionConflict, ad.best_match, req, ws)
+
+ # If more than one match on the path, the first one takes precedence
+ ws = WorkingSet([]); ws.add(foo14); ws.add(foo12); ws.add(foo14);
+ self.assertEqual(ad.best_match(req,ws).version, '1.4')
+
+ def checkFooPkg(self,d):
+ self.assertEqual(d.project_name, "FooPkg")
+ self.assertEqual(d.key, "foopkg")
+ self.assertEqual(d.version, "1.3-1")
+ self.assertEqual(d.py_version, "2.4")
+ self.assertEqual(d.platform, "win32")
+ self.assertEqual(d.parsed_version, parse_version("1.3-1"))
+
+ def testDistroBasics(self):
+ d = Distribution(
+ "/some/path",
+ project_name="FooPkg",version="1.3-1",py_version="2.4",platform="win32"
+ )
+ self.checkFooPkg(d)
+
+ d = Distribution("/some/path")
+ self.assertEqual(d.py_version, sys.version[:3])
+ self.assertEqual(d.platform, None)
+
+ def testDistroParse(self):
+ d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg")
+ self.checkFooPkg(d)
+ d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg-info")
+ self.checkFooPkg(d)
+
+ def testDistroMetadata(self):
+ d = Distribution(
+ "/some/path", project_name="FooPkg", py_version="2.4", platform="win32",
+ metadata = Metadata(
+ ('PKG-INFO',"Metadata-Version: 1.0\nVersion: 1.3-1\n")
+ )
+ )
+ self.checkFooPkg(d)
+
+
+ def distRequires(self, txt):
+ return Distribution("/foo", metadata=Metadata(('depends.txt', txt)))
+
+ def checkRequires(self, dist, txt, extras=()):
+ self.assertEqual(
+ list(dist.requires(extras)),
+ list(parse_requirements(txt))
+ )
+
+ def testDistroDependsSimple(self):
+ for v in "Twisted>=1.5", "Twisted>=1.5\nZConfig>=2.0":
+ self.checkRequires(self.distRequires(v), v)
+
+
+ def testResolve(self):
+ ad = Environment([]); ws = WorkingSet([])
+ # Resolving no requirements -> nothing to install
+ self.assertEqual( list(ws.resolve([],ad)), [] )
+ # Request something not in the collection -> DistributionNotFound
+ self.assertRaises(
+ DistributionNotFound, ws.resolve, parse_requirements("Foo"), ad
+ )
+ Foo = Distribution.from_filename(
+ "/foo_dir/Foo-1.2.egg",
+ metadata=Metadata(('depends.txt', "[bar]\nBaz>=2.0"))
+ )
+ ad.add(Foo); ad.add(Distribution.from_filename("Foo-0.9.egg"))
+
+ # Request thing(s) that are available -> list to activate
+ for i in range(3):
+ targets = list(ws.resolve(parse_requirements("Foo"), ad))
+ self.assertEqual(targets, [Foo])
+ map(ws.add,targets)
+ self.assertRaises(VersionConflict, ws.resolve,
+ parse_requirements("Foo==0.9"), ad)
+ ws = WorkingSet([]) # reset
+
+ # Request an extra that causes an unresolved dependency for "Baz"
+ self.assertRaises(
+ DistributionNotFound, ws.resolve,parse_requirements("Foo[bar]"), ad
+ )
+ Baz = Distribution.from_filename(
+ "/foo_dir/Baz-2.1.egg", metadata=Metadata(('depends.txt', "Foo"))
+ )
+ ad.add(Baz)
+
+ # Activation list now includes resolved dependency
+ self.assertEqual(
+ list(ws.resolve(parse_requirements("Foo[bar]"), ad)), [Foo,Baz]
+ )
+ # Requests for conflicting versions produce VersionConflict
+ self.assertRaises( VersionConflict,
+ ws.resolve, parse_requirements("Foo==1.2\nFoo!=1.2"), ad
+ )
+
+ def testDistroDependsOptions(self):
+ d = self.distRequires("""
+ Twisted>=1.5
+ [docgen]
+ ZConfig>=2.0
+ docutils>=0.3
+ [fastcgi]
+ fcgiapp>=0.1""")
+ self.checkRequires(d,"Twisted>=1.5")
+ self.checkRequires(
+ d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3".split(), ["docgen"]
+ )
+ self.checkRequires(
+ d,"Twisted>=1.5 fcgiapp>=0.1".split(), ["fastcgi"]
+ )
+ self.checkRequires(
+ d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3 fcgiapp>=0.1".split(),
+ ["docgen","fastcgi"]
+ )
+ self.checkRequires(
+ d,"Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(),
+ ["fastcgi", "docgen"]
+ )
+ self.assertRaises(UnknownExtra, d.requires, ["foo"])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class EntryPointTests(TestCase):
+
+ def assertfields(self, ep):
+ self.assertEqual(ep.name,"foo")
+ self.assertEqual(ep.module_name,"setuptools.tests.test_resources")
+ self.assertEqual(ep.attrs, ("EntryPointTests",))
+ self.assertEqual(ep.extras, ("x",))
+ self.failUnless(ep.load() is EntryPointTests)
+ self.assertEqual(
+ str(ep),
+ "foo = setuptools.tests.test_resources:EntryPointTests [x]"
+ )
+
+ def setUp(self):
+ self.dist = Distribution.from_filename(
+ "FooPkg-1.2-py2.4.egg", metadata=Metadata(('requires.txt','[x]')))
+
+ def testBasics(self):
+ ep = EntryPoint(
+ "foo", "setuptools.tests.test_resources", ["EntryPointTests"],
+ ["x"], self.dist
+ )
+ self.assertfields(ep)
+
+ def testParse(self):
+ s = "foo = setuptools.tests.test_resources:EntryPointTests [x]"
+ ep = EntryPoint.parse(s, self.dist)
+ self.assertfields(ep)
+
+ ep = EntryPoint.parse("bar baz= spammity[PING]")
+ self.assertEqual(ep.name,"bar baz")
+ self.assertEqual(ep.module_name,"spammity")
+ self.assertEqual(ep.attrs, ())
+ self.assertEqual(ep.extras, ("ping",))
+
+ ep = EntryPoint.parse(" fizzly = wocka:foo")
+ self.assertEqual(ep.name,"fizzly")
+ self.assertEqual(ep.module_name,"wocka")
+ self.assertEqual(ep.attrs, ("foo",))
+ self.assertEqual(ep.extras, ())
+
+ def testRejects(self):
+ for ep in [
+ "foo", "x=1=2", "x=a:b:c", "q=x/na", "fez=pish:tush-z", "x=f[a]>2",
+ ]:
+ try: EntryPoint.parse(ep)
+ except ValueError: pass
+ else: raise AssertionError("Should've been bad", ep)
+
+ def checkSubMap(self, m):
+ self.assertEqual(str(m),
+ "{"
+ "'feature2': EntryPoint.parse("
+ "'feature2 = another.module:SomeClass [extra1,extra2]'), "
+ "'feature1': EntryPoint.parse("
+ "'feature1 = somemodule:somefunction')"
+ "}"
+ )
+
+ submap_str = """
+ # define features for blah blah
+ feature1 = somemodule:somefunction
+ feature2 = another.module:SomeClass [extra1,extra2]
+ """
+
+ def testParseList(self):
+ self.checkSubMap(EntryPoint.parse_group("xyz", self.submap_str))
+ self.assertRaises(ValueError, EntryPoint.parse_group, "x a", "foo=bar")
+ self.assertRaises(ValueError, EntryPoint.parse_group, "x",
+ ["foo=baz", "foo=bar"])
+
+ def testParseMap(self):
+ m = EntryPoint.parse_map({'xyz':self.submap_str})
+ self.checkSubMap(m['xyz'])
+ self.assertEqual(m.keys(),['xyz'])
+ m = EntryPoint.parse_map("[xyz]\n"+self.submap_str)
+ self.checkSubMap(m['xyz'])
+ self.assertEqual(m.keys(),['xyz'])
+ self.assertRaises(ValueError, EntryPoint.parse_map, ["[xyz]", "[xyz]"])
+ self.assertRaises(ValueError, EntryPoint.parse_map, self.submap_str)
+
+
+class RequirementsTests(TestCase):
+
+ def testBasics(self):
+ r = Requirement.parse("Twisted>=1.2")
+ self.assertEqual(str(r),"Twisted>=1.2")
+ self.assertEqual(repr(r),"Requirement.parse('Twisted>=1.2')")
+ self.assertEqual(r, Requirement("Twisted", [('>=','1.2')], ()))
+ self.assertEqual(r, Requirement("twisTed", [('>=','1.2')], ()))
+ self.assertNotEqual(r, Requirement("Twisted", [('>=','2.0')], ()))
+ self.assertNotEqual(r, Requirement("Zope", [('>=','1.2')], ()))
+ self.assertNotEqual(r, Requirement("Zope", [('>=','3.0')], ()))
+ self.assertNotEqual(r, Requirement.parse("Twisted[extras]>=1.2"))
+
+ def testOrdering(self):
+ r1 = Requirement("Twisted", [('==','1.2c1'),('>=','1.2')], ())
+ r2 = Requirement("Twisted", [('>=','1.2'),('==','1.2c1')], ())
+ self.assertEqual(r1,r2)
+ self.assertEqual(str(r1),str(r2))
+ self.assertEqual(str(r2),"Twisted==1.2c1,>=1.2")
+
+ def testBasicContains(self):
+ r = Requirement("Twisted", [('>=','1.2')], ())
+ foo_dist = Distribution.from_filename("FooPkg-1.3_1.egg")
+ twist11 = Distribution.from_filename("Twisted-1.1.egg")
+ twist12 = Distribution.from_filename("Twisted-1.2.egg")
+ self.failUnless(parse_version('1.2') in r)
+ self.failUnless(parse_version('1.1') not in r)
+ self.failUnless('1.2' in r)
+ self.failUnless('1.1' not in r)
+ self.failUnless(foo_dist not in r)
+ self.failUnless(twist11 not in r)
+ self.failUnless(twist12 in r)
+
+ def testAdvancedContains(self):
+ r, = parse_requirements("Foo>=1.2,<=1.3,==1.9,>2.0,!=2.5,<3.0,==4.5")
+ for v in ('1.2','1.2.2','1.3','1.9','2.0.1','2.3','2.6','3.0c1','4.5'):
+ self.failUnless(v in r, (v,r))
+ for v in ('1.2c1','1.3.1','1.5','1.9.1','2.0','2.5','3.0','4.0'):
+ self.failUnless(v not in r, (v,r))
+
+
+ def testOptionsAndHashing(self):
+ r1 = Requirement.parse("Twisted[foo,bar]>=1.2")
+ r2 = Requirement.parse("Twisted[bar,FOO]>=1.2")
+ r3 = Requirement.parse("Twisted[BAR,FOO]>=1.2.0")
+ self.assertEqual(r1,r2)
+ self.assertEqual(r1,r3)
+ self.assertEqual(r1.extras, ("foo","bar"))
+ self.assertEqual(r2.extras, ("bar","foo")) # extras are normalized
+ self.assertEqual(hash(r1), hash(r2))
+ self.assertEqual(
+ hash(r1), hash(("twisted", ((">=",parse_version("1.2")),),
+ ImmutableSet(["foo","bar"])))
+ )
+
+ def testVersionEquality(self):
+ r1 = Requirement.parse("setuptools==0.3a2")
+ r2 = Requirement.parse("setuptools!=0.3a4")
+ d = Distribution.from_filename
+
+ self.failIf(d("setuptools-0.3a4.egg") in r1)
+ self.failIf(d("setuptools-0.3a1.egg") in r1)
+ self.failIf(d("setuptools-0.3a4.egg") in r2)
+
+ self.failUnless(d("setuptools-0.3a2.egg") in r1)
+ self.failUnless(d("setuptools-0.3a2.egg") in r2)
+ self.failUnless(d("setuptools-0.3a3.egg") in r2)
+ self.failUnless(d("setuptools-0.3a5.egg") in r2)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class ParseTests(TestCase):
+
+ def testEmptyParse(self):
+ self.assertEqual(list(parse_requirements('')), [])
+
+ def testYielding(self):
+ for inp,out in [
+ ([], []), ('x',['x']), ([[]],[]), (' x\n y', ['x','y']),
+ (['x\n\n','y'], ['x','y']),
+ ]:
+ self.assertEqual(list(pkg_resources.yield_lines(inp)),out)
+
+ def testSplitting(self):
+ self.assertEqual(
+ list(
+ pkg_resources.split_sections("""
+ x
+ [Y]
+ z
+
+ a
+ [b ]
+ # foo
+ c
+ [ d]
+ [q]
+ v
+ """
+ )
+ ),
+ [(None,["x"]), ("Y",["z","a"]), ("b",["c"]), ("d",[]), ("q",["v"])]
+ )
+ self.assertRaises(ValueError,list,pkg_resources.split_sections("[foo"))
+
+ def testSafeName(self):
+ self.assertEqual(safe_name("adns-python"), "adns-python")
+ self.assertEqual(safe_name("WSGI Utils"), "WSGI-Utils")
+ self.assertEqual(safe_name("WSGI Utils"), "WSGI-Utils")
+ self.assertEqual(safe_name("Money$$$Maker"), "Money-Maker")
+ self.assertNotEqual(safe_name("peak.web"), "peak-web")
+
+ def testSafeVersion(self):
+ self.assertEqual(safe_version("1.2-1"), "1.2-1")
+ self.assertEqual(safe_version("1.2 alpha"), "1.2.alpha")
+ self.assertEqual(safe_version("2.3.4 20050521"), "2.3.4.20050521")
+ self.assertEqual(safe_version("Money$$$Maker"), "Money-Maker")
+ self.assertEqual(safe_version("peak.web"), "peak.web")
+
+ def testSimpleRequirements(self):
+ self.assertEqual(
+ list(parse_requirements('Twis-Ted>=1.2-1')),
+ [Requirement('Twis-Ted',[('>=','1.2-1')], ())]
+ )
+ self.assertEqual(
+ list(parse_requirements('Twisted >=1.2, \ # more\n<2.0')),
+ [Requirement('Twisted',[('>=','1.2'),('<','2.0')], ())]
+ )
+ self.assertEqual(
+ Requirement.parse("FooBar==1.99a3"),
+ Requirement("FooBar", [('==','1.99a3')], ())
+ )
+ self.assertRaises(ValueError,Requirement.parse,">=2.3")
+ self.assertRaises(ValueError,Requirement.parse,"x\\")
+ self.assertRaises(ValueError,Requirement.parse,"x==2 q")
+ self.assertRaises(ValueError,Requirement.parse,"X==1\nY==2")
+ self.assertRaises(ValueError,Requirement.parse,"#")
+
+ def testVersionEquality(self):
+ def c(s1,s2):
+ p1, p2 = parse_version(s1),parse_version(s2)
+ self.assertEqual(p1,p2, (s1,s2,p1,p2))
+
+ c('1.2-rc1', '1.2rc1')
+ c('0.4', '0.4.0')
+ c('0.4.0.0', '0.4.0')
+ c('0.4.0-0', '0.4-0')
+ c('0pl1', '0.0pl1')
+ c('0pre1', '0.0c1')
+ c('0.0.0preview1', '0c1')
+ c('0.0c1', '0-rc1')
+ c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a')
+
+ def testVersionOrdering(self):
+ def c(s1,s2):
+ p1, p2 = parse_version(s1),parse_version(s2)
+ self.failUnless(p1<p2, (s1,s2,p1,p2))
+
+ c('2.1','2.1.1')
+ c('2a1','2b0')
+ c('2a1','2.1')
+ c('2.3a1', '2.3')
+ c('2.1-1', '2.1-2')
+ c('2.1-1', '2.1.1')
+ c('2.1', '2.1pl4')
+ c('2.1a0-20040501', '2.1')
+ c('1.1', '02.1')
+ c('A56','B27')
+ c('3.2', '3.2.pl0')
+ c('3.2-1', '3.2pl1')
+ c('3.2pl1', '3.2pl1-1')
+ c('0.4', '4.0')
+ c('0.0.4', '0.4.0')
+ c('0pl1', '0.4pl1')
+ c('2.1.0-rc1','2.1.0')
+
+ torture ="""
+ 0.80.1-3 0.80.1-2 0.80.1-1 0.79.9999+0.80.0pre4-1
+ 0.79.9999+0.80.0pre2-3 0.79.9999+0.80.0pre2-2
+ 0.77.2-1 0.77.1-1 0.77.0-1
+ """.split()
+
+ for p,v1 in enumerate(torture):
+ for v2 in torture[p+1:]:
+ c(v2,v1)