aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.hgtags2
-rw-r--r--CHANGES.txt20
-rwxr-xr-xREADME.txt24
-rw-r--r--distribute_setup.py2
-rw-r--r--docs/conf.py2
-rw-r--r--docs/easy_install.txt13
-rw-r--r--pkg_resources.py10
-rwxr-xr-xrelease.sh4
-rwxr-xr-xsetup.py2
-rw-r--r--setuptools/__init__.py13
-rwxr-xr-xsetuptools/command/develop.py109
-rwxr-xr-xsetuptools/command/easy_install.py168
-rwxr-xr-xsetuptools/command/install_scripts.py4
-rw-r--r--setuptools/command/upload_docs.py15
-rwxr-xr-xsetuptools/sandbox.py7
-rw-r--r--setuptools/tests/__init__.py64
-rw-r--r--setuptools/tests/test_develop.py24
-rw-r--r--setuptools/tests/test_easy_install.py96
-rw-r--r--setuptools/tests/test_resources.py38
-rw-r--r--setuptools/tests/test_sandbox.py11
-rw-r--r--setuptools/tests/test_upload_docs.py22
-rw-r--r--test.sh6
22 files changed, 430 insertions, 226 deletions
diff --git a/.hgtags b/.hgtags
index c904a24a..eb90dab5 100644
--- a/.hgtags
+++ b/.hgtags
@@ -13,3 +13,5 @@ ac7d9b14ac43fecb8b65de548b25773553facaee 0.6.9
0fd5c506037880409308f2b79c6e901d21e7fe92 0.6.10
0fd5c506037880409308f2b79c6e901d21e7fe92 0.6.10
f18396c6e1875476279d8bbffd8e6dadcc695136 0.6.10
+e00987890c0b386f09d0f6b73d8558b72f6367f1 0.6.11
+48a97bc89e2f65fc9b78b358d7dc89ba9ec9524a 0.6.12
diff --git a/CHANGES.txt b/CHANGES.txt
index eeab73c6..ad743ae7 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -3,6 +3,18 @@ CHANGES
=======
------
+0.6.13
+------
+
+*
+
+------
+0.6.12
+------
+
+* Issue 149: Fixed various failures on 2.3/2.4
+
+------
0.6.11
------
@@ -11,7 +23,13 @@ CHANGES
* Added indexsidebar.html into MANIFEST.in
* Issue 108: Fixed TypeError with Python3.1
* Issue 121: Fixed --help install command trying to actually install.
-* Issue 112: Added an os.makedirs so that Tarek's solution will work.
+* Issue 112: Added an os.makedirs so that Tarek's solution will work.
+* Issue 133: Added --no-find-links to easy_install
+* Added easy_install --user
+* Issue 100: Fixed develop --user not taking '.' in PYTHONPATH into account
+* Issue 134: removed spurious UserWarnings. Patch by VanLindberg
+* Issue 138: cant_write_to_target error when setup_requires is used.
+* Issue 147: respect the sys.dont_write_bytecode flag
------
0.6.10
diff --git a/README.txt b/README.txt
index 8e78bc88..72d897e7 100755
--- a/README.txt
+++ b/README.txt
@@ -68,9 +68,8 @@ Installation Instructions
Distribute is only released as a source distribution.
-It can be installed using easy_install or pip, and can be done so with the source
-tarball, the eggs distribution, or by using the ``distribute_setup.py`` script
-provided online.
+It can be installed using pip, and can be done so with the source tarball,
+or by using the ``distribute_setup.py`` script provided online.
``distribute_setup.py`` is the simplest and preferred way on all systems.
@@ -88,28 +87,21 @@ If your shell has the ``curl`` program you can do::
Notice this file is also provided in the source release.
-easy_install or pip
-===================
+pip
+===
Run easy_install or pip::
- $ easy_install -U distribute
$ pip install distribute
-If you want to install the latest dev version, you can also run::
-
- $ easy_install -U distribute==dev
-
-This will get the latest development version at: http://bitbucket.org/tarek/distribute/get/0.6-maintenance.zip#egg=distribute-dev
-
Source installation
===================
Download the source tarball, uncompress it, then run the install command::
- $ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.11.tar.gz
- $ tar -xzvf distribute-0.6.11.tar.gz
- $ cd distribute-0.6.11
+ $ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.13.tar.gz
+ $ tar -xzvf distribute-0.6.13.tar.gz
+ $ cd distribute-0.6.13
$ python setup.py install
---------------------------
@@ -128,8 +120,6 @@ Distribute is installed in three steps:
Distribute can be removed like this:
-- run ``easy_install -m Distribute``. This will remove the Distribute reference
- from ``easy-install.pth``. Otherwise, edit the file and remove it yourself.
- remove the ``distribute*.egg`` file located in your site-packages directory
- remove the ``setuptools.pth`` file located in you site-packages directory
- remove the easy_install script located in you ``sys.prefix/bin`` directory
diff --git a/distribute_setup.py b/distribute_setup.py
index 3c23affa..3cb043e2 100644
--- a/distribute_setup.py
+++ b/distribute_setup.py
@@ -46,7 +46,7 @@ except ImportError:
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
-DEFAULT_VERSION = "0.6.11"
+DEFAULT_VERSION = "0.6.13"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
diff --git a/docs/conf.py b/docs/conf.py
index 3991c73c..53614ed5 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -48,7 +48,7 @@ copyright = u'2009, The fellowship of the packaging'
# built documents.
#
# The short X.Y version.
-version = '0.6.11'
+version = '0.6.13'
# The full version, including alpha/beta/rc tags.
release = '0.6.4'
diff --git a/docs/easy_install.txt b/docs/easy_install.txt
index 3e39b811..ab008a1d 100644
--- a/docs/easy_install.txt
+++ b/docs/easy_install.txt
@@ -144,6 +144,10 @@ and Viewing Source Packages`_ below for more info.)::
easy_install --editable --build-directory ~/projects SQLObject
+**Example 7**. (New in 0.6.11) Install a distribution within your home dir::
+
+ easy_install --user SQLAlchemy
+
Easy Install accepts URLs, filenames, PyPI package names (i.e., ``distutils``
"distribution" names), and package+version specifiers. In each case, it will
attempt to locate the latest available version that meets your criteria.
@@ -716,6 +720,10 @@ Command-Line Options
versions of a package, but do not want to reset the version that will be
run by scripts that are already installed.
+``--user`` (New in 0.6.11)
+ Use the the user-site-packages as specified in :pep:`370`
+ instead of the global site-packages.
+
``--always-copy, -a`` (New in 0.5a4)
Copy all needed distributions to the installation directory, even if they
are already present in a directory on sys.path. In older versions of
@@ -768,6 +776,11 @@ Command-Line Options
package not being available locally, or due to the use of the ``--update``
or ``-U`` option.
+``--no-find-links`` Blocks the addition of any link. (New in Distribute 0.6.11)
+ This is useful if you want to avoid adding links defined in a project
+ easy_install is installing (wether it's a requested project or a
+ dependency.). When used, ``--find-links`` is ignored.
+
``--delete-conflicting, -D`` (Removed in 0.6a11)
(As of 0.6a11, this option is no longer necessary; please do not use it!)
diff --git a/pkg_resources.py b/pkg_resources.py
index f31789e9..6ec51fa0 100644
--- a/pkg_resources.py
+++ b/pkg_resources.py
@@ -191,9 +191,10 @@ def _macosx_vers(_cache=[]):
import plistlib
plist = '/System/Library/CoreServices/SystemVersion.plist'
if os.path.exists(plist):
- plist_content = plistlib.readPlist(plist)
- if 'ProductVersion' in plist_content:
- version = plist_content['ProductVersion']
+ if hasattr(plistlib, 'readPlist'):
+ plist_content = plistlib.readPlist(plist)
+ if 'ProductVersion' in plist_content:
+ version = plist_content['ProductVersion']
_cache.append(version.split('.'))
return _cache[0]
@@ -2332,7 +2333,8 @@ class Distribution(object):
if modname in ('pkg_resources', 'setuptools', 'site'):
continue
fn = getattr(sys.modules[modname], '__file__', None)
- if fn and normalize_path(fn).startswith(loc):
+ if fn and (normalize_path(fn).startswith(loc) or
+ fn.startswith(self.location)):
continue
issue_warning(
"Module %s was already imported from %s, but %s is being added"
diff --git a/release.sh b/release.sh
index cdcba01f..8388e993 100755
--- a/release.sh
+++ b/release.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-export VERSION="0.6.11"
+export VERSION="0.6.12"
# tagging
hg tag $VERSION
@@ -16,7 +16,7 @@ cd ..
python2.6 setup.py upload_docs
# pushing the bootstrap script
-scp distribute_setup.py ziade.org:nightly/build/
+scp distribute_setup.py ziade.org:websites/python-distribute.org/
# starting the new dev
hg push
diff --git a/setup.py b/setup.py
index 064c2afd..3ae40817 100755
--- a/setup.py
+++ b/setup.py
@@ -37,7 +37,7 @@ init_path = convert_path('setuptools/command/__init__.py')
exec(open(init_path).read(), d)
SETUP_COMMANDS = d['__all__']
-VERSION = "0.6.11"
+VERSION = "0.6.13"
from setuptools import setup, find_packages
from setuptools.command.build_py import build_py as _build_py
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index d03d0bf0..9de373f9 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -5,7 +5,8 @@ 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
+import os
+import sys
__version__ = '0.6'
__all__ = [
@@ -19,7 +20,8 @@ __all__ = [
# a distribution with the same version.
#
# The distribute_setup script for instance, will check if this
-# attribute is present to decide wether to reinstall the package
+# attribute is present to decide whether to reinstall the package
+# or not.
_distribute = True
bootstrap_install_from = None
@@ -94,4 +96,9 @@ def findall(dir = os.curdir):
import distutils.filelist
distutils.filelist.findall = findall # fix findall bug in distutils.
-
+# sys.dont_write_bytecode was introduced in Python 2.6.
+if ((hasattr(sys, "dont_write_bytecode") and sys.dont_write_bytecode) or
+ (not hasattr(sys, "dont_write_bytecode") and os.environ.get("PYTHONDONTWRITEBYTECODE"))):
+ _dont_write_bytecode = True
+else:
+ _dont_write_bytecode = False
diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py
index 330ba168..93b7773c 100755
--- a/setuptools/command/develop.py
+++ b/setuptools/command/develop.py
@@ -2,19 +2,8 @@ from setuptools.command.easy_install import easy_install
from distutils.util import convert_path, subst_vars
from pkg_resources import Distribution, PathMetadata, normalize_path
from distutils import log
-from distutils.errors import *
-import sys, os, setuptools, glob
-from distutils.sysconfig import get_config_vars
-from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS
-
-if sys.version < "2.6":
- USER_BASE = None
- USER_SITE = None
- HAS_USER_SITE = False
-else:
- from site import USER_BASE
- from site import USER_SITE
- HAS_USER_SITE = True
+from distutils.errors import DistutilsError, DistutilsOptionError
+import os, setuptools, glob
class develop(easy_install):
"""Set up package for development"""
@@ -28,11 +17,6 @@ class develop(easy_install):
boolean_options = easy_install.boolean_options + ['uninstall']
- if HAS_USER_SITE:
- user_options.append(('user', None,
- "install in user site-package '%s'" % USER_SITE))
- boolean_options.append('user')
-
command_consumes_arguments = False # override base
def run(self):
@@ -49,56 +33,8 @@ class develop(easy_install):
easy_install.initialize_options(self)
self.setup_path = None
self.always_copy_from = '.' # always copy eggs installed in curdir
- self.user = 0
- self.install_purelib = None # for pure module distributions
- self.install_platlib = None # non-pure (dists w/ extensions)
- self.install_headers = None # for C/C++ headers
- self.install_lib = None # set to either purelib or platlib
- self.install_scripts = None
- self.install_data = None
- self.install_base = None
- self.install_platbase = None
- self.install_userbase = USER_BASE
- self.install_usersite = USER_SITE
-
- def select_scheme(self, name):
- """Sets the install directories by applying the install schemes."""
- # it's the caller's problem if they supply a bad name!
- scheme = INSTALL_SCHEMES[name]
- for key in SCHEME_KEYS:
- attrname = 'install_' + key
- if getattr(self, attrname) is None:
- setattr(self, attrname, scheme[key])
-
- def create_home_path(self):
- """Create directories under ~."""
- if not self.user:
- return
- home = convert_path(os.path.expanduser("~"))
- for name, path in self.config_vars.iteritems():
- if path.startswith(home) and not os.path.isdir(path):
- self.debug_print("os.makedirs('%s', 0700)" % path)
- os.makedirs(path, 0700)
-
- def _expand_attrs(self, attrs):
- for attr in attrs:
- val = getattr(self, attr)
- if val is not None:
- if os.name == 'posix' or os.name == 'nt':
- val = os.path.expanduser(val)
- val = subst_vars(val, self.config_vars)
- setattr(self, attr, val)
-
- def expand_basedirs(self):
- """Calls `os.path.expanduser` on install_base, install_platbase and
- root."""
- self._expand_attrs(['install_base', 'install_platbase', 'root'])
-
- def expand_dirs(self):
- """Calls `os.path.expanduser` on install dirs."""
- self._expand_attrs(['install_purelib', 'install_platlib',
- 'install_lib', 'install_headers',
- 'install_scripts', 'install_data',])
+
+
def finalize_options(self):
ei = self.get_finalized_command("egg_info")
@@ -110,44 +46,11 @@ class develop(easy_install):
self.args = [ei.egg_name]
- py_version = sys.version.split()[0]
- prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix')
- self.config_vars = {'dist_name': self.distribution.get_name(),
- 'dist_version': self.distribution.get_version(),
- 'dist_fullname': self.distribution.get_fullname(),
- 'py_version': py_version,
- 'py_version_short': py_version[0:3],
- 'py_version_nodot': py_version[0] + py_version[2],
- 'sys_prefix': prefix,
- 'prefix': prefix,
- 'sys_exec_prefix': exec_prefix,
- 'exec_prefix': exec_prefix,
- }
-
- if HAS_USER_SITE:
- self.config_vars['userbase'] = self.install_userbase
- self.config_vars['usersite'] = self.install_usersite
-
- # fix the install_dir if "--user" was used
- if self.user:
- self.create_home_path()
- if self.install_userbase is None:
- raise DistutilsPlatformError(
- "User base directory is not specified")
- self.install_base = self.install_platbase = self.install_userbase
- if os.name == 'posix':
- self.select_scheme("unix_user")
- else:
- self.select_scheme(os.name + "_user")
-
- self.expand_basedirs()
- self.expand_dirs()
- if self.user and self.install_purelib:
- self.install_dir = self.install_purelib
- self.script_dir = self.install_scripts
easy_install.finalize_options(self)
+ self.expand_basedirs()
+ self.expand_dirs()
# pick up setup-dir .egg files only: no .egg-info
self.package_index.scan(glob.glob('*.egg'))
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index 366ac7bc..8aab6f1e 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -7,21 +7,30 @@ 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
+__ http://packages.python.org/distribute/easy_install.html
+
"""
-import sys, os, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random
+import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random
from glob import glob
-from setuptools import Command
+from setuptools import Command, _dont_write_bytecode
from setuptools.sandbox import run_setup
from distutils import log, dir_util
-from distutils.sysconfig import get_python_lib
+from distutils.util import convert_path, subst_vars
+from distutils.sysconfig import get_python_lib, get_config_vars
from distutils.errors import DistutilsArgError, DistutilsOptionError, \
- DistutilsError
+ DistutilsError, DistutilsPlatformError
+from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS
from setuptools.archive_util import unpack_archive
-from setuptools.package_index import PackageIndex, parse_bdist_wininst
+from setuptools.package_index import PackageIndex
from setuptools.package_index import URL_SCHEME
from setuptools.command import bdist_egg, egg_info
-from pkg_resources import *
+from pkg_resources import yield_lines, normalize_path, resource_string, \
+ ensure_directory, get_distribution, find_distributions, \
+ Environment, Requirement, Distribution, \
+ PathMetadata, EggMetadata, WorkingSet, \
+ DistributionNotFound, VersionConflict, \
+ DEVELOP_DIST
+
sys_executable = os.path.normpath(sys.executable)
__all__ = [
@@ -29,6 +38,9 @@ __all__ = [
'main', 'get_exe_prefixes',
]
+import site
+HAS_USER_SITE = not sys.version < "2.6"
+
def samefile(p1,p2):
if hasattr(os.path,'samefile') and (
os.path.exists(p1) and os.path.exists(p2)
@@ -91,16 +103,32 @@ class easy_install(Command):
('allow-hosts=', 'H', "pattern(s) that hostnames must match"),
('local-snapshots-ok', 'l', "allow building eggs from local checkouts"),
('version', None, "print version information and exit"),
+ ('no-find-links', None,
+ "Don't load find-links defined in packages being installed")
]
boolean_options = [
'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',
'delete-conflicting', 'ignore-conflicts-at-my-risk', 'editable',
'no-deps', 'local-snapshots-ok', 'version'
]
+
+ if HAS_USER_SITE:
+ user_options.append(('user', None,
+ "install in user site-package '%s'" % site.USER_SITE))
+ boolean_options.append('user')
+
+
negative_opt = {'always-unzip': 'zip-ok'}
create_index = PackageIndex
def initialize_options(self):
+ if HAS_USER_SITE:
+ whereami = os.path.abspath(__file__)
+ self.user = (whereami.startswith(site.USER_SITE)
+ or whereami.startswith(site.USER_BASE))
+ else:
+ self.user = 0
+
self.zip_ok = self.local_snapshots_ok = None
self.install_dir = self.script_dir = self.exclude_scripts = None
self.index_url = None
@@ -112,6 +140,21 @@ class easy_install(Command):
self.editable = self.no_deps = self.allow_hosts = None
self.root = self.prefix = self.no_report = None
self.version = None
+ self.install_purelib = None # for pure module distributions
+ self.install_platlib = None # non-pure (dists w/ extensions)
+ self.install_headers = None # for C/C++ headers
+ self.install_lib = None # set to either purelib or platlib
+ self.install_scripts = None
+ self.install_data = None
+ self.install_base = None
+ self.install_platbase = None
+ if HAS_USER_SITE:
+ self.install_userbase = site.USER_BASE
+ self.install_usersite = site.USER_SITE
+ else:
+ self.install_userbase = None
+ self.install_usersite = None
+ self.no_find_links = None
# Options not specifiable via command line
self.package_index = None
@@ -147,12 +190,50 @@ class easy_install(Command):
print 'distribute %s' % get_distribution('distribute').version
sys.exit()
+ py_version = sys.version.split()[0]
+ prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix')
+
+ self.config_vars = {'dist_name': self.distribution.get_name(),
+ 'dist_version': self.distribution.get_version(),
+ 'dist_fullname': self.distribution.get_fullname(),
+ 'py_version': py_version,
+ 'py_version_short': py_version[0:3],
+ 'py_version_nodot': py_version[0] + py_version[2],
+ 'sys_prefix': prefix,
+ 'prefix': prefix,
+ 'sys_exec_prefix': exec_prefix,
+ 'exec_prefix': exec_prefix,
+ }
+
+ if HAS_USER_SITE:
+ self.config_vars['userbase'] = self.install_userbase
+ self.config_vars['usersite'] = self.install_usersite
+
+ # fix the install_dir if "--user" was used
+ #XXX: duplicate of the code in the setup command
+ if self.user and HAS_USER_SITE:
+ self.create_home_path()
+ if self.install_userbase is None:
+ raise DistutilsPlatformError(
+ "User base directory is not specified")
+ self.install_base = self.install_platbase = self.install_userbase
+ if os.name == 'posix':
+ self.select_scheme("unix_user")
+ else:
+ self.select_scheme(os.name + "_user")
+
+ self.expand_basedirs()
+ self.expand_dirs()
+
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
+ if self.no_find_links is None:
+ self.no_find_links = False
+
# 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.
@@ -163,6 +244,10 @@ class easy_install(Command):
self.set_undefined_options('install_scripts',
('install_dir', 'script_dir')
)
+
+ if self.user and self.install_purelib:
+ self.install_dir = self.install_purelib
+ self.script_dir = self.install_scripts
# default --record from the install command
self.set_undefined_options('install', ('record', 'record'))
normpath = map(normalize_path, sys.path)
@@ -204,7 +289,8 @@ class easy_install(Command):
self.find_links = []
if self.local_snapshots_ok:
self.package_index.scan_egg_links(self.shadow_path+sys.path)
- self.package_index.add_find_links(self.find_links)
+ if not self.no_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:
@@ -228,8 +314,29 @@ class easy_install(Command):
self.outputs = []
+
+ def _expand_attrs(self, attrs):
+ for attr in attrs:
+ val = getattr(self, attr)
+ if val is not None:
+ if os.name == 'posix' or os.name == 'nt':
+ val = os.path.expanduser(val)
+ val = subst_vars(val, self.config_vars)
+ setattr(self, attr, val)
+
+ def expand_basedirs(self):
+ """Calls `os.path.expanduser` on install_base, install_platbase and
+ root."""
+ self._expand_attrs(['install_base', 'install_platbase', 'root'])
+
+ def expand_dirs(self):
+ """Calls `os.path.expanduser` on install dirs."""
+ self._expand_attrs(['install_purelib', 'install_platlib',
+ 'install_lib', 'install_headers',
+ 'install_scripts', 'install_data',])
+
def run(self):
- if self.verbose<>self.distribution.verbose:
+ if self.verbose != self.distribution.verbose:
log.set_verbosity(self.verbose)
try:
for spec in self.args:
@@ -271,6 +378,7 @@ class easy_install(Command):
def check_site_dir(self):
"""Verify that self.install_dir is .pth-capable dir, if needed"""
+ print 'install_dir', self.install_dir
instdir = normalize_path(self.install_dir)
pth_file = os.path.join(instdir,'easy-install.pth')
@@ -342,7 +450,7 @@ variable.
For information on other options, you may wish to consult the
documentation at:
- http://peak.telecommunity.com/EasyInstall.html
+ http://packages.python.org/distribute/easy_install.html
Please make the appropriate changes for your system and try again.
"""
@@ -360,7 +468,9 @@ Please make the appropriate changes for your system and try again.
ok_exists = os.path.exists(ok_file)
try:
if ok_exists: os.unlink(ok_file)
- os.makedirs(os.path.dirname(ok_file))
+ dirname = os.path.dirname(ok_file)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
f = open(pth_file,'w')
except (OSError,IOError):
self.cant_write_to_target()
@@ -513,6 +623,15 @@ Please make the appropriate changes for your system and try again.
+ def select_scheme(self, name):
+ """Sets the install directories by applying the install schemes."""
+ # it's the caller's problem if they supply a bad name!
+ scheme = INSTALL_SCHEMES[name]
+ for key in SCHEME_KEYS:
+ attrname = 'install_' + key
+ if getattr(self, attrname) is None:
+ setattr(self, attrname, scheme[key])
+
@@ -523,7 +642,8 @@ Please make the appropriate changes for your system and try again.
self.install_egg_scripts(dist)
self.installed_projects[dist.key] = dist
log.info(self.installation_report(requirement, dist, *info))
- if dist.has_metadata('dependency_links.txt'):
+ if (dist.has_metadata('dependency_links.txt') and
+ not self.no_find_links):
self.package_index.add_find_links(
dist.get_metadata_lines('dependency_links.txt')
)
@@ -1029,6 +1149,10 @@ See the setuptools documentation for the "develop" command for more info.
chmod(f, mode)
def byte_compile(self, to_compile):
+ if _dont_write_bytecode:
+ self.warn('byte-compiling is disabled, skipping.')
+ return
+
from distutils.util import byte_compile
try:
# try to make the byte compile messages quieter
@@ -1076,7 +1200,7 @@ Here are some of your options for correcting the problem:
* 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
+ http://packages.python.org/distribute/easy_install.html#custom-installation-locations
Please make the appropriate changes for your system and try again.""" % (
self.install_dir, os.environ.get('PYTHONPATH','')
@@ -1130,7 +1254,15 @@ Please make the appropriate changes for your system and try again.""" % (
-
+ def create_home_path(self):
+ """Create directories under ~."""
+ if not self.user:
+ return
+ home = convert_path(os.path.expanduser("~"))
+ for name, path in self.config_vars.iteritems():
+ if path.startswith(home) and not os.path.isdir(path):
+ self.debug_print("os.makedirs('%s', 0700)" % path)
+ os.makedirs(path, 0700)
@@ -1421,8 +1553,12 @@ class PthDistributions(Environment):
def add(self,dist):
"""Add `dist` to the distribution map"""
- if dist.location not in self.paths and dist.location not in self.sitedirs:
- self.paths.append(dist.location); self.dirty = True
+ if (dist.location not in self.paths and (
+ dist.location not in self.sitedirs or
+ dist.location == os.getcwd() #account for '.' being in PYTHONPATH
+ )):
+ self.paths.append(dist.location)
+ self.dirty = True
Environment.add(self,dist)
def remove(self,dist):
diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py
index ac797883..6ce1b993 100755
--- a/setuptools/command/install_scripts.py
+++ b/setuptools/command/install_scripts.py
@@ -12,8 +12,8 @@ class install_scripts(_install_scripts):
self.no_ep = False
def run(self):
- from setuptools.command.easy_install import (get_script_args,
- sys_executable)
+ from setuptools.command.easy_install import get_script_args
+ from setuptools.command.easy_install import sys_executable
self.run_command("egg_info")
if self.distribution.scripts:
diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py
index e961a0df..213f7b58 100644
--- a/setuptools/command/upload_docs.py
+++ b/setuptools/command/upload_docs.py
@@ -12,10 +12,17 @@ import httplib
import base64
import urlparse
import tempfile
+import sys
from distutils import log
from distutils.errors import DistutilsOptionError
-from distutils.command.upload import upload
+
+try:
+ from distutils.command.upload import upload
+except ImportError:
+ from setuptools.command.upload import upload
+
+_IS_PYTHON3 = sys.version > '3'
try:
bytes
@@ -89,10 +96,10 @@ class upload_docs(upload):
}
# set up the authentication
credentials = self.username + ':' + self.password
- try: # base64 only works with bytes in Python 3.
+ if _IS_PYTHON3: # base64 only works with bytes in Python 3.
encoded_creds = base64.encodebytes(credentials.encode('utf8'))
- auth = b"Basic "
- except AttributeError:
+ auth = bytes("Basic ")
+ else:
encoded_creds = base64.encodestring(credentials)
auth = "Basic "
auth += encoded_creds.strip()
diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py
index b2ae991f..37fc60af 100755
--- a/setuptools/sandbox.py
+++ b/setuptools/sandbox.py
@@ -152,7 +152,10 @@ class AbstractSandbox:
)
-_EXCEPTIONS = [os.devnull,]
+if hasattr(os, 'devnull'):
+ _EXCEPTIONS = [os.devnull,]
+else:
+ _EXCEPTIONS = []
try:
win32com_pkg = os.path.dirname(__import__('win32com').__file__)
@@ -209,7 +212,7 @@ class DirectorySandbox(AbstractSandbox):
def _exempted(self, filepath):
exception_matches = map(filepath.startswith, self._exceptions)
- return any(exception_matches)
+ return True in exception_matches
def _remap_input(self,operation,path,*args,**kw):
"""Called for path inputs"""
diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py
index f540103e..4fb382ca 100644
--- a/setuptools/tests/__init__.py
+++ b/setuptools/tests/__init__.py
@@ -94,21 +94,21 @@ class DependsTests(TestCase):
from email 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.assertTrue(req.version_ok('1.0.9'))
+ self.assertFalse(req.version_ok('0.9.1'))
+ self.assertFalse(req.version_ok('unknown'))
- self.failUnless(req.is_present())
- self.failUnless(req.is_current())
+ self.assertTrue(req.is_present())
+ self.assertTrue(req.is_current())
req = Require('Email 3000','03000','email',format=LooseVersion)
- self.failUnless(req.is_present())
- self.failIf(req.is_current())
- self.failIf(req.version_ok('unknown'))
+ self.assertTrue(req.is_present())
+ self.assertFalse(req.is_current())
+ self.assertFalse(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())
+ self.assertFalse(req.is_present())
+ self.assertFalse(req.is_current())
req = Require('Tests', None, 'tests', homepage="http://example.com")
self.assertEqual(req.format, None)
@@ -118,8 +118,8 @@ class DependsTests(TestCase):
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))
+ self.assertTrue(req.is_present(paths))
+ self.assertTrue(req.is_current(paths))
class DistroTests(TestCase):
@@ -137,7 +137,7 @@ class DistroTests(TestCase):
def testDistroType(self):
- self.failUnless(isinstance(self.dist,setuptools.dist.Distribution))
+ self.assertTrue(isinstance(self.dist,setuptools.dist.Distribution))
def testExcludePackage(self):
@@ -189,17 +189,17 @@ class DistroTests(TestCase):
dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2])
def testContents(self):
- self.failUnless(self.dist.has_contents_for('a'))
+ self.assertTrue(self.dist.has_contents_for('a'))
self.dist.exclude_package('a')
- self.failIf(self.dist.has_contents_for('a'))
+ self.assertFalse(self.dist.has_contents_for('a'))
- self.failUnless(self.dist.has_contents_for('b'))
+ self.assertTrue(self.dist.has_contents_for('b'))
self.dist.exclude_package('b')
- self.failIf(self.dist.has_contents_for('b'))
+ self.assertFalse(self.dist.has_contents_for('b'))
- self.failUnless(self.dist.has_contents_for('c'))
+ self.assertTrue(self.dist.has_contents_for('c'))
self.dist.exclude_package('c')
- self.failIf(self.dist.has_contents_for('c'))
+ self.assertFalse(self.dist.has_contents_for('c'))
@@ -269,12 +269,12 @@ class FeatureTests(TestCase):
)
def testDefaults(self):
- self.failIf(
+ self.assertFalse(
Feature(
"test",standard=True,remove='x',available=False
).include_by_default()
)
- self.failUnless(
+ self.assertTrue(
Feature("test",standard=True,remove='x').include_by_default()
)
# Feature must have either kwargs, removes, or require_features
@@ -288,33 +288,33 @@ class FeatureTests(TestCase):
def testFeatureOptions(self):
dist = self.dist
- self.failUnless(
+ self.assertTrue(
('with-dwim',None,'include DWIM') in dist.feature_options
)
- self.failUnless(
+ self.assertTrue(
('without-dwim',None,'exclude DWIM (default)') in dist.feature_options
)
- self.failUnless(
+ self.assertTrue(
('with-bar',None,'include bar (default)') in dist.feature_options
)
- self.failUnless(
+ self.assertTrue(
('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)
+ self.assertFalse('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.assertFalse('bar_et' in dist.py_modules)
+ self.assertFalse('pkg.bar' in dist.packages)
+ self.assertTrue('pkg.baz' in dist.packages)
+ self.assertTrue('scripts/baz_it' in dist.scripts)
+ self.assertTrue(('libfoo','foo/foofoo.c') in dist.libraries)
self.assertEqual(dist.ext_modules,[])
self.assertEqual(dist.require_features, [self.req])
@@ -331,7 +331,7 @@ class TestCommandTests(TestCase):
def testTestIsCommand(self):
test_cmd = makeSetup().get_command_obj('test')
- self.failUnless(isinstance(test_cmd, distutils.cmd.Command))
+ self.assertTrue(isinstance(test_cmd, distutils.cmd.Command))
def testLongOptSuiteWNoDefault(self):
ts1 = makeSetup(script_args=['test','--test-suite=foo.tests.suite'])
diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py
index 1d5fa141..a567dd5a 100644
--- a/setuptools/tests/test_develop.py
+++ b/setuptools/tests/test_develop.py
@@ -6,8 +6,9 @@ import tempfile
import site
from StringIO import StringIO
+from distutils.errors import DistutilsError
from setuptools.command.develop import develop
-from setuptools.command import develop as develop_pkg
+from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution
SETUP_PY = """\
@@ -28,9 +29,9 @@ class TestDevelopTest(unittest.TestCase):
os.chdir(self.dir)
if sys.version >= "2.6":
self.old_base = site.USER_BASE
- site.USER_BASE = develop_pkg.USER_BASE = tempfile.mkdtemp()
+ site.USER_BASE = tempfile.mkdtemp()
self.old_site = site.USER_SITE
- site.USER_SITE = develop_pkg.USER_SITE = tempfile.mkdtemp()
+ site.USER_SITE = tempfile.mkdtemp()
def tearDown(self):
os.chdir(self.old_cwd)
@@ -49,6 +50,7 @@ class TestDevelopTest(unittest.TestCase):
cmd = develop(dist)
cmd.user = 1
cmd.ensure_finalized()
+ cmd.install_dir = site.USER_SITE
cmd.user = 1
old_stdout = sys.stdout
sys.stdout = StringIO()
@@ -62,3 +64,19 @@ class TestDevelopTest(unittest.TestCase):
content.sort()
self.assertEquals(content, ['UNKNOWN.egg-link', 'easy-install.pth'])
+ def test_develop_with_setup_requires(self):
+
+ wanted = ("Could not find suitable distribution for "
+ "Requirement.parse('I-DONT-EXIST')")
+ old_dir = os.getcwd()
+ os.chdir(self.dir)
+ try:
+ try:
+ dist = Distribution({'setup_requires': ['I_DONT_EXIST']})
+ except DistutilsError, e:
+ error = str(e)
+ if error == wanted:
+ pass
+ finally:
+ os.chdir(old_dir)
+
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
index 95909ca7..e02798c6 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -2,8 +2,13 @@
"""
import sys
import os, shutil, tempfile, unittest
+import site
+from StringIO import StringIO
from setuptools.command.easy_install import easy_install, get_script_args, main
+from setuptools.command.easy_install import PthDistributions
+from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution
+from pkg_resources import Distribution as PRDistribution
class FakeDist(object):
def get_entry_map(self, group):
@@ -90,3 +95,94 @@ class TestEasyInstallTest(unittest.TestCase):
os.chdir(old_wd)
shutil.rmtree(dir)
+ def test_no_find_links(self):
+ # new option '--no-find-links', that blocks find-links added at
+ # the project level
+ dist = Distribution()
+ cmd = easy_install(dist)
+ cmd.check_pth_processing = lambda : True
+ cmd.no_find_links = True
+ cmd.find_links = ['link1', 'link2']
+ cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok')
+ cmd.args = ['ok']
+ cmd.ensure_finalized()
+ self.assertEquals(cmd.package_index.scanned_urls, {})
+
+ # let's try without it (default behavior)
+ cmd = easy_install(dist)
+ cmd.check_pth_processing = lambda : True
+ cmd.find_links = ['link1', 'link2']
+ cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok')
+ cmd.args = ['ok']
+ cmd.ensure_finalized()
+ keys = cmd.package_index.scanned_urls.keys()
+ keys.sort()
+ self.assertEquals(keys, ['link1', 'link2'])
+
+
+class TestPTHFileWriter(unittest.TestCase):
+ def test_add_from_cwd_site_sets_dirty(self):
+ '''a pth file manager should set dirty
+ if a distribution is in site but also the cwd
+ '''
+ pth = PthDistributions('does-not_exist', [os.getcwd()])
+ self.assert_(not pth.dirty)
+ pth.add(PRDistribution(os.getcwd()))
+ self.assert_(pth.dirty)
+
+ def test_add_from_site_is_ignored(self):
+ pth = PthDistributions('does-not_exist', ['/test/location/does-not-have-to-exist'])
+ self.assert_(not pth.dirty)
+ pth.add(PRDistribution('/test/location/does-not-have-to-exist'))
+ self.assert_(not pth.dirty)
+
+
+class TestUserInstallTest(unittest.TestCase):
+
+ def setUp(self):
+ self.dir = tempfile.mkdtemp()
+ setup = os.path.join(self.dir, 'setup.py')
+ f = open(setup, 'w')
+ f.write(SETUP_PY)
+ f.close()
+ self.old_cwd = os.getcwd()
+ os.chdir(self.dir)
+ if sys.version >= "2.6":
+ self.old_base = site.USER_BASE
+ site.USER_BASE = tempfile.mkdtemp()
+ self.old_site = site.USER_SITE
+ site.USER_SITE = tempfile.mkdtemp()
+
+ def tearDown(self):
+ os.chdir(self.old_cwd)
+ shutil.rmtree(self.dir)
+ if sys.version >= "2.6":
+ shutil.rmtree(site.USER_BASE)
+ shutil.rmtree(site.USER_SITE)
+ site.USER_BASE = self.old_base
+ site.USER_SITE = self.old_site
+
+ def test_install(self):
+ #XXX: replace with something meaningfull
+ return
+ if sys.version < "2.6":
+ return
+ dist = Distribution()
+ dist.script_name = 'setup.py'
+ cmd = easy_install(dist)
+ cmd.user = 1
+ cmd.args = ['py']
+ cmd.ensure_finalized()
+ cmd.user = 1
+ old_stdout = sys.stdout
+ sys.stdout = StringIO()
+ try:
+ cmd.run()
+ finally:
+ sys.stdout = old_stdout
+
+ # let's see if we got our egg link at the right place
+ content = os.listdir(site.USER_SITE)
+ content.sort()
+ self.assertEquals(content, ['UNKNOWN.egg-link', 'easy-install.pth'])
+
diff --git a/setuptools/tests/test_resources.py b/setuptools/tests/test_resources.py
index 5cb1baf0..c1dc4887 100644
--- a/setuptools/tests/test_resources.py
+++ b/setuptools/tests/test_resources.py
@@ -35,7 +35,7 @@ class DistroTests(TestCase):
ad.add(Distribution.from_filename("FooPkg-1.2-py2.4.egg"))
# Name is in there now
- self.failUnless(ad['FooPkg'])
+ self.assertTrue(ad['FooPkg'])
# But only 1 package
self.assertEqual(list(ad), ['foopkg'])
@@ -218,7 +218,7 @@ class EntryPointTests(TestCase):
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.assertTrue(ep.load() is EntryPointTests)
self.assertEqual(
str(ep),
"foo = setuptools.tests.test_resources:EntryPointTests [x]"
@@ -318,20 +318,20 @@ class RequirementsTests(TestCase):
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)
+ self.assertTrue(parse_version('1.2') in r)
+ self.assertTrue(parse_version('1.1') not in r)
+ self.assertTrue('1.2' in r)
+ self.assertTrue('1.1' not in r)
+ self.assertTrue(foo_dist not in r)
+ self.assertTrue(twist11 not in r)
+ self.assertTrue(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))
+ self.assertTrue(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))
+ self.assertTrue(v not in r, (v,r))
def testOptionsAndHashing(self):
@@ -353,14 +353,14 @@ class RequirementsTests(TestCase):
r2 = Requirement.parse("foo!=0.3a4")
d = Distribution.from_filename
- self.failIf(d("foo-0.3a4.egg") in r1)
- self.failIf(d("foo-0.3a1.egg") in r1)
- self.failIf(d("foo-0.3a4.egg") in r2)
+ self.assertFalse(d("foo-0.3a4.egg") in r1)
+ self.assertFalse(d("foo-0.3a1.egg") in r1)
+ self.assertFalse(d("foo-0.3a4.egg") in r2)
- self.failUnless(d("foo-0.3a2.egg") in r1)
- self.failUnless(d("foo-0.3a2.egg") in r2)
- self.failUnless(d("foo-0.3a3.egg") in r2)
- self.failUnless(d("foo-0.3a5.egg") in r2)
+ self.assertTrue(d("foo-0.3a2.egg") in r1)
+ self.assertTrue(d("foo-0.3a2.egg") in r2)
+ self.assertTrue(d("foo-0.3a3.egg") in r2)
+ self.assertTrue(d("foo-0.3a5.egg") in r2)
def testDistributeSetuptoolsOverride(self):
# Plain setuptools or distribute mean we return distribute.
@@ -480,7 +480,7 @@ class ParseTests(TestCase):
def testVersionOrdering(self):
def c(s1,s2):
p1, p2 = parse_version(s1),parse_version(s2)
- self.failUnless(p1<p2, (s1,s2,p1,p2))
+ self.assertTrue(p1<p2, (s1,s2,p1,p2))
c('2.1','2.1.1')
c('2a1','2b0')
diff --git a/setuptools/tests/test_sandbox.py b/setuptools/tests/test_sandbox.py
index 8b9e08e6..1609ee86 100644
--- a/setuptools/tests/test_sandbox.py
+++ b/setuptools/tests/test_sandbox.py
@@ -30,10 +30,11 @@ class TestSandbox(unittest.TestCase):
shutil.rmtree(self.dir)
def test_devnull(self):
+ if sys.version < '2.4':
+ return
sandbox = DirectorySandbox(self.dir)
sandbox.run(self._file_writer(os.devnull))
- @staticmethod
def _file_writer(path):
def do_write():
f = open(path, 'w')
@@ -41,6 +42,7 @@ class TestSandbox(unittest.TestCase):
f.close()
return do_write
+ _file_writer = staticmethod(_file_writer)
if has_win32com():
def test_win32com(self):
@@ -53,9 +55,10 @@ class TestSandbox(unittest.TestCase):
target = os.path.join(gen_py, 'test_write')
sandbox = DirectorySandbox(self.dir)
try:
- sandbox.run(self._file_writer(target))
- except SandboxViolation:
- self.fail("Could not create gen_py file due to SandboxViolation")
+ try:
+ sandbox.run(self._file_writer(target))
+ except SandboxViolation:
+ self.fail("Could not create gen_py file due to SandboxViolation")
finally:
if os.path.exists(target): os.remove(target)
diff --git a/setuptools/tests/test_upload_docs.py b/setuptools/tests/test_upload_docs.py
index 15db899f..8b2dc892 100644
--- a/setuptools/tests/test_upload_docs.py
+++ b/setuptools/tests/test_upload_docs.py
@@ -19,24 +19,24 @@ class TestUploadDocsTest(unittest.TestCase):
f.close()
self.old_cwd = os.getcwd()
os.chdir(self.dir)
-
+
self.upload_dir = os.path.join(self.dir, 'build')
os.mkdir(self.upload_dir)
-
+
# A test document.
f = open(os.path.join(self.upload_dir, 'index.html'), 'w')
f.write("Hello world.")
f.close()
-
+
# An empty folder.
os.mkdir(os.path.join(self.upload_dir, 'empty'))
-
+
if sys.version >= "2.6":
self.old_base = site.USER_BASE
site.USER_BASE = upload_docs.USER_BASE = tempfile.mkdtemp()
self.old_site = site.USER_SITE
site.USER_SITE = upload_docs.USER_SITE = tempfile.mkdtemp()
-
+
def tearDown(self):
os.chdir(self.old_cwd)
shutil.rmtree(self.dir)
@@ -49,17 +49,17 @@ class TestUploadDocsTest(unittest.TestCase):
def test_create_zipfile(self):
# Test to make sure zipfile creation handles common cases.
# This explicitly includes a folder containing an empty folder.
-
+
dist = Distribution()
-
+
cmd = upload_docs(dist)
cmd.upload_dir = self.upload_dir
zip_file = cmd.create_zipfile()
-
+
assert zipfile.is_zipfile(zip_file)
-
+
zip_f = zipfile.ZipFile(zip_file) # woh...
-
+
assert zip_f.namelist() == ['index.html']
-
+
diff --git a/test.sh b/test.sh
new file mode 100644
index 00000000..ac309dec
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,6 @@
+python2.3 setup.py test
+python2.4 setup.py test
+python2.5 setup.py test
+python2.6 setup.py test
+python3.1 setup.py test
+