[Python-checkins] distutils2: merged from upstream
tarek.ziade
python-checkins at python.org
Sun Sep 19 10:20:21 CEST 2010
tarek.ziade pushed 10da588ede3e to distutils2:
http://hg.python.org/distutils2/rev/10da588ede3e
changeset: 608:10da588ede3e
parent: 479:7debb069fc21
parent: 607:59d10bf5a35c
user: Konrad Delong <konryd at gmail.com>
date: Mon Aug 09 13:09:23 2010 +0200
summary: merged from upstream
files: docs/source/commands.rst, src/distutils2/command/upload_docs.py, src/distutils2/dist.py
diff --git a/docs/source/commands.rst b/docs/source/commands.rst
--- a/docs/source/commands.rst
+++ b/docs/source/commands.rst
@@ -6,6 +6,55 @@
You might recognize some of them from other projects, like Distribute or
Setuptools.
+``test`` - Build package and run a unittest suite
+=================================================
+
+When doing test-driven development, or running automated builds that need
+testing before they are deployed for downloading or use, it's often useful
+to be able to run a project's unit tests without actually deploying the project
+anywhere, even using the ``develop`` command. The ``test`` command runs a
+project's unit tests without actually deploying it, by temporarily putting the
+project's source on ``sys.path``, after first running ``build_ext -i`` and
+``egg_info`` to ensure that any C extensions and project metadata are
+up-to-date.
+
+To use this command, your project's tests must be wrapped in a ``unittest``
+test suite by either a function, a ``TestCase`` class or method, or a module
+or package containing ``TestCase`` classes. If the named suite is a module,
+and the module has an ``additional_tests()`` function, it is called and the
+result (which must be a ``unittest.TestSuite``) is added to the tests to be
+run. If the named suite is a package, any submodules and subpackages are
+recursively added to the overall test suite. (Note: if your project specifies
+a ``test_loader``, the rules for processing the chosen ``test_suite`` may
+differ; see the `test_loader`_ documentation for more details.)
+
+Note that many test systems including ``doctest`` support wrapping their
+non-``unittest`` tests in ``TestSuite`` objects. So, if you are using a test
+package that does not support this, we suggest you encourage its developers to
+implement test suite support, as this is a convenient and standard way to
+aggregate a collection of tests to be run under a common test harness.
+
+By default, tests will be run in the "verbose" mode of the ``unittest``
+package's text test runner, but you can get the "quiet" mode (just dots) if
+you supply the ``-q`` or ``--quiet`` option, either as a global option to
+the setup script (e.g. ``setup.py -q test``) or as an option for the ``test``
+command itself (e.g. ``setup.py test -q``). There is one other option
+available:
+
+``--test-suite=NAME, -s NAME``
+ Specify the test suite (or module, class, or method) to be run
+ (e.g. ``some_module.test_suite``). The default for this option can be
+ set by giving a ``test_suite`` argument to the ``setup()`` function, e.g.::
+
+ setup(
+ # ...
+ test_suite = "my_package.tests.test_all"
+ )
+
+ If you did not set a ``test_suite`` in your ``setup()`` call, and do not
+ provide a ``--test-suite`` option, an error will occur.
+
+
``upload`` - Upload source and/or binary distributions to PyPI
==============================================================
@@ -84,7 +133,7 @@
python setup.py upload_docs --upload-dir=docs/build/html
-As with any other command, you can define useful
+As with any other ``setuptools`` based command, you can define useful
defaults in the ``setup.cfg`` of your Python project, e.g.:
.. code-block:: ini
diff --git a/src/distutils2/_backport/__init__.py b/src/distutils2/_backport/__init__.py
--- a/src/distutils2/_backport/__init__.py
+++ b/src/distutils2/_backport/__init__.py
@@ -1,2 +1,8 @@
"""Things that will land in the Python 3.3 std lib but which we must drag along
with us for now to support 2.x."""
+
+def any(seq):
+ for elem in seq:
+ if elem:
+ return elem
+ return False
diff --git a/src/distutils2/command/test.py b/src/distutils2/command/test.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/command/test.py
@@ -0,0 +1,65 @@
+import os, sys
+from distutils2.core import Command
+from distutils2._backport.pkgutil import get_distribution
+import unittest
+import warnings
+
+def get_loader_instance(dotted_path):
+ if dotted_path is None:
+ return None
+ module_name, rest = dotted_path.split('.')[0], dotted_path.split('.')[1:]
+ while True:
+ try:
+ ret = __import__(module_name)
+ break
+ except ImportError:
+ if rest == []:
+ return None
+ module_name += ('.' + rest[0])
+ rest = rest[1:]
+ while rest:
+ try:
+ ret = getattr(ret, rest.pop(0))
+ except AttributeError:
+ return None
+ return ret()
+
+class test(Command):
+
+ description = "" # TODO
+ user_options = [
+ ('test-suite=', 's',
+ "Test suite to run (e.g. 'some_module.test_suite')"),
+ ('test-loader=', None,
+ "Test loader to be used to load the test suite."),
+ ]
+
+ def initialize_options(self):
+ self.test_suite = None
+ self.test_loader = None
+
+ def finalize_options(self):
+ self.build_lib = self.get_finalized_command("build").build_lib
+ if self.distribution.tests_require:
+ for requirement in self.distribution.tests_require:
+ if get_distribution(requirement) is None:
+ warnings.warn("The test dependency %s is not installed which may couse the tests to fail.",
+ RuntimeWarning)
+
+ def run(self):
+ prev_cwd = os.getcwd()
+ try:
+ if self.distribution.has_ext_modules():
+ build = self.get_reinitialized_command('build')
+ build.inplace = 1 # TODO - remove make sure it's needed
+ self.run_command('build')
+ os.chdir(self.build_lib)
+ args = {"module": self.test_suite,
+ "argv": sys.argv[:1],
+ "testLoader": get_loader_instance(self.test_loader)
+ }
+ if args['testLoader'] is None:
+ del args['testLoader']
+ unittest.main(**args)
+ finally:
+ os.chdir(prev_cwd)
diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py
--- a/src/distutils2/command/upload_docs.py
+++ b/src/distutils2/command/upload_docs.py
@@ -94,9 +94,10 @@
def run(self):
name = self.distribution.metadata['Name']
+ version = self.distribution.metadata['Version']
zip_file = zip_dir(self.upload_dir)
- fields = {':action': 'doc_upload', 'name': name}.items()
+ fields = [(':action', 'doc_upload'), ('name', name), ('version', version)]
files = [('content', name, zip_file.getvalue())]
content_type, body = encode_multipart(fields, files)
diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py
--- a/src/distutils2/dist.py
+++ b/src/distutils2/dist.py
@@ -110,6 +110,8 @@
"print the list of packages/modules provided"),
('requires', None,
"print the list of packages/modules required"),
+ ('tests-require', None,
+ "print the list of packages/modules required to run the test suite"),
('obsoletes', None,
"print the list of packages/modules made obsolete"),
('use-2to3', None,
diff --git a/src/distutils2/tests/dists/custom_loader/myowntestmodule.py b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/custom_loader/myowntestmodule.py
@@ -0,0 +1,9 @@
+import unittest
+class Loader(object):
+ def loadTestsFromModule(self, names, module=None):
+ class SomeTest(unittest.TestCase):
+ def test_blah(self):
+ self.fail("horribly")
+ return unittest.makeSuite(SomeTest)
+ def __repr__(self):
+ return 'YO'
diff --git a/src/distutils2/tests/dists/custom_loader/setup.cfg b/src/distutils2/tests/dists/custom_loader/setup.cfg
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/custom_loader/setup.cfg
@@ -0,0 +1,2 @@
+[test]
+test-loader = myowntestmodule.Loader
diff --git a/src/distutils2/tests/dists/custom_loader/setup.py b/src/distutils2/tests/dists/custom_loader/setup.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/custom_loader/setup.py
@@ -0,0 +1,5 @@
+from distutils2.core import setup
+setup(name='somedist',
+ version='0.1',
+ py_modules=['myowntestmodule', 'somemod'],
+)
diff --git a/src/distutils2/tests/dists/extensions_test/myowntestmodule.py b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/extensions_test/myowntestmodule.py
@@ -0,0 +1,4 @@
+import unittest
+class SomeTest(unittest.TestCase):
+ def test_blah(self):
+ self.fail("horribly")
diff --git a/src/distutils2/tests/dists/extensions_test/setup.cfg b/src/distutils2/tests/dists/extensions_test/setup.cfg
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/extensions_test/setup.cfg
@@ -0,0 +1,2 @@
+[test]
+test-suite = myowntestmodule
diff --git a/src/distutils2/tests/dists/extensions_test/setup.py b/src/distutils2/tests/dists/extensions_test/setup.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/extensions_test/setup.py
@@ -0,0 +1,6 @@
+from distutils2.core import setup, Extension
+setup(name='somedist',
+ version='0.1',
+ py_modules=['myowntestmodule', 'somemod'],
+ ext_modules=[Extension('spam', ['spammodule.c'])],
+)
diff --git a/src/distutils2/tests/dists/extensions_test/spammodule.c b/src/distutils2/tests/dists/extensions_test/spammodule.c
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/extensions_test/spammodule.c
@@ -0,0 +1,11 @@
+#include <Python.h>
+
+static PyMethodDef SpamMethods[] = {
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+PyMODINIT_FUNC
+initspam(void)
+{
+ (void) Py_InitModule("spam", SpamMethods);
+}
diff --git a/src/distutils2/tests/dists/simple_test/myowntestmodule.py b/src/distutils2/tests/dists/simple_test/myowntestmodule.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/simple_test/myowntestmodule.py
@@ -0,0 +1,4 @@
+import unittest
+class SomeTest(unittest.TestCase):
+ def test_blah(self):
+ self.fail("horribly")
diff --git a/src/distutils2/tests/dists/simple_test/setup.cfg b/src/distutils2/tests/dists/simple_test/setup.cfg
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/simple_test/setup.cfg
@@ -0,0 +1,2 @@
+[test]
+test-suite = myowntestmodule
diff --git a/src/distutils2/tests/dists/simple_test/setup.py b/src/distutils2/tests/dists/simple_test/setup.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/dists/simple_test/setup.py
@@ -0,0 +1,5 @@
+from distutils2.core import setup
+setup(name='somedist',
+ version='0.1',
+ py_modules=['myowntestmodule', 'somemod'],
+)
diff --git a/src/distutils2/tests/test_test.py b/src/distutils2/tests/test_test.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/test_test.py
@@ -0,0 +1,102 @@
+import os, sys, shutil, re
+from copy import copy
+from os.path import join
+from StringIO import StringIO
+from distutils2.tests.support import unittest, TempdirManager
+from distutils2.command.test import test
+from distutils2.dist import Distribution
+import subprocess
+
+try:
+ any
+except NameError:
+ from distutils2._backport import any
+
+EXPECTED_OUTPUT_RE = r'''FAIL: test_blah \(myowntestmodule.SomeTest\)
+----------------------------------------------------------------------
+Traceback \(most recent call last\):
+ File ".+/myowntestmodule.py", line \d+, in test_blah
+ self.fail\("horribly"\)
+AssertionError: horribly
+'''
+
+here = os.path.dirname(os.path.abspath(__file__))
+
+
+class TestTest(TempdirManager, unittest.TestCase):
+
+ def setUp(self):
+ super(TestTest, self).setUp()
+
+ distutils2path = os.path.dirname(os.path.dirname(here))
+ self.old_pythonpath = os.environ.get('PYTHONPATH', '')
+ os.environ['PYTHONPATH'] = distutils2path + os.pathsep + self.old_pythonpath
+
+ def tearDown(self):
+ os.environ['PYTHONPATH'] = self.old_pythonpath
+ super(TestTest, self).tearDown()
+
+ def assert_re_match(self, pattern, string):
+ def quote(s):
+ lines = ['## ' + line for line in s.split('\n')]
+ sep = ["#" * 60]
+ return [''] + sep + lines + sep
+ msg = quote(pattern) + ["didn't match"] + quote(string)
+ msg = "\n".join(msg)
+ if not re.search(pattern, string):
+ self.fail(msg)
+
+ def run_with_dist_cwd(self, pkg_dir):
+ cwd = os.getcwd()
+ command = [sys.executable, "setup.py", "test"]
+ try:
+ os.chdir(pkg_dir)
+ test_proc = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+ _, errors = test_proc.communicate()
+ return errors
+ finally:
+ os.chdir(cwd)
+
+ def prepare_dist(self, dist_name):
+ pkg_dir = join(os.path.dirname(__file__), "dists", dist_name)
+ temp_pkg_dir = join(self.mkdtemp(), dist_name)
+ shutil.copytree(pkg_dir, temp_pkg_dir)
+ return temp_pkg_dir
+
+ def test_runs_simple_tests(self):
+ self.pkg_dir = self.prepare_dist('simple_test')
+ output = self.run_with_dist_cwd(self.pkg_dir)
+ self.assert_re_match(EXPECTED_OUTPUT_RE, output)
+ self.assertFalse(os.path.exists(join(self.pkg_dir, 'build')))
+
+ def test_builds_extensions(self):
+ self.pkg_dir = self.prepare_dist("extensions_test")
+ output = self.run_with_dist_cwd(self.pkg_dir)
+ self.assert_re_match(EXPECTED_OUTPUT_RE, output)
+ self.assertTrue(os.path.exists(join(self.pkg_dir, 'build')))
+ self.assertTrue(any(x.startswith('lib') for x in os.listdir(join(self.pkg_dir, 'build'))))
+
+ def test_custom_test_loader(self):
+ self.pkg_dir = self.prepare_dist("custom_loader")
+ output = self.run_with_dist_cwd(self.pkg_dir)
+ self.assert_re_match(EXPECTED_OUTPUT_RE, output)
+
+ def _test_works_with_2to3(self):
+ pass
+
+ @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6')
+ def test_checks_requires(self):
+ from distutils2.tests.with_support import examine_warnings
+ dist = Distribution()
+ dist.tests_require = ['ohno_ohno-impossible_1234-name_stop-that!']
+ cmd = test(dist)
+ def examinator(ws):
+ cmd.ensure_finalized()
+ self.assertEqual(1, len(ws))
+ warning = ws[0]
+ self.assertIs(warning.category, RuntimeWarning)
+
+ examine_warnings(examinator)
+
+def test_suite():
+ return unittest.makeSuite(TestTest)
diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py
--- a/src/distutils2/tests/test_upload_docs.py
+++ b/src/distutils2/tests/test_upload_docs.py
@@ -116,12 +116,13 @@
self.assertTrue(handler.headers.dict['content-type']
.startswith('multipart/form-data;'))
- action, name, content =\
- request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4]
+ action, name, version, content =\
+ request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:5]
# check that we picked the right chunks
self.assertIn('name=":action"', action)
self.assertIn('name="name"', name)
+ self.assertIn('name="version"', version)
self.assertIn('name="content"', content)
# check their contents
diff --git a/src/distutils2/tests/with_support.py b/src/distutils2/tests/with_support.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/with_support.py
@@ -0,0 +1,7 @@
+def examine_warnings(examinator):
+ """Given an examinator function calls it as if the code was under with
+ catch_warnings block. Useful for testing older Python versions"""
+ import warnings
+ warnings.simplefilter('default')
+ with warnings.catch_warnings(record=True) as ws:
+ examinator(ws)
--
Repository URL: http://hg.python.org/distutils2
More information about the Python-checkins
mailing list