← Back to team overview

testtools-dev team mailing list archive

[Merge] lp:~lifeless/testtools/useextras into lp:testtools

 

Robert Collins has proposed merging lp:~lifeless/testtools/useextras into lp:testtools.

Requested reviews:
  testtools committers (testtools-committers)

For more details, see:
https://code.launchpad.net/~lifeless/testtools/useextras/+merge/143653

We split extras out. Now use it.
-- 
https://code.launchpad.net/~lifeless/testtools/useextras/+merge/143653
Your team testtools developers is subscribed to branch lp:testtools.
=== modified file 'NEWS'
--- NEWS	2012-12-19 12:10:59 +0000
+++ NEWS	2013-01-17 09:28:19 +0000
@@ -6,6 +6,15 @@
 NEXT
 ~~~~
 
+Changes
+-------
+
+* Testtools now depends on extras, a small library split out from it to contain
+  generally useful non-testing facilities. Since extras has been around for a
+  couple of testtools releases now, we're making this into a hard dependency of
+  testtools. The ``safe_hasattr``, ``try_import`` and ``try_imports`` symbols
+  are no longer exported from testtools. (Robert Collins)
+
 0.9.24
 ~~~~~~
 

=== modified file 'doc/for-test-authors.rst'
--- doc/for-test-authors.rst	2012-11-29 11:40:17 +0000
+++ doc/for-test-authors.rst	2013-01-17 09:28:19 +0000
@@ -1288,7 +1288,7 @@
 -------------------
 
 Lots of the time we would like to conditionally import modules.  testtools
-needs to do this itself, and graciously extends the ability to its users.
+uses the small library extras to do this. This used to be part of testtools.
 
 Instead of::
 
@@ -1317,9 +1317,9 @@
 Safe attribute testing
 ----------------------
 
-``hasattr`` is broken_ on many versions of Python.  testtools provides
-``safe_hasattr``, which can be used to safely test whether an object has a
-particular attribute.
+``hasattr`` is broken_ on many versions of Python. The helper ``safe_hasattr``
+can be used to safely test whether an object has a particular attribute. Like
+``try_import`` this used to be in testtools but is now in extras.
 
 
 Nullary callables

=== modified file 'testtools/__init__.py'
--- testtools/__init__.py	2012-12-19 12:10:59 +0000
+++ testtools/__init__.py	2013-01-17 09:28:19 +0000
@@ -26,14 +26,8 @@
     'skipIf',
     'skipUnless',
     'ThreadsafeForwardingResult',
-    'try_import',
-    'try_imports',
     ]
 
-from testtools.helpers import (
-    try_import,
-    try_imports,
-    )
 from testtools.matchers._impl import (
     Matcher,
     )

=== modified file 'testtools/compat.py'
--- testtools/compat.py	2012-10-20 17:44:19 +0000
+++ testtools/compat.py	2013-01-17 09:28:19 +0000
@@ -27,7 +27,7 @@
 import traceback
 import unicodedata
 
-from testtools.helpers import try_imports
+from extras import try_imports
 
 BytesIO = try_imports(['StringIO.StringIO', 'io.BytesIO'])
 StringIO = try_imports(['StringIO.StringIO', 'io.StringIO'])

=== modified file 'testtools/content.py'
--- testtools/content.py	2012-12-10 22:57:03 +0000
+++ testtools/content.py	2013-01-17 09:28:19 +0000
@@ -17,7 +17,8 @@
 import sys
 import traceback
 
-from testtools import try_import
+from extras import try_import
+
 from testtools.compat import _b, _format_exc_info, str_is_unicode, _u
 from testtools.content_type import ContentType, JSON, UTF8_TEXT
 

=== modified file 'testtools/helpers.py'
--- testtools/helpers.py	2012-08-10 14:05:57 +0000
+++ testtools/helpers.py	2013-01-17 09:28:19 +0000
@@ -1,92 +1,11 @@
 # Copyright (c) 2010-2012 testtools developers. See LICENSE for details.
 
 __all__ = [
-    'safe_hasattr',
-    'try_import',
-    'try_imports',
     ]
 
 import sys
 
 
-def try_import(name, alternative=None, error_callback=None):
-    """Attempt to import ``name``.  If it fails, return ``alternative``.
-
-    When supporting multiple versions of Python or optional dependencies, it
-    is useful to be able to try to import a module.
-
-    :param name: The name of the object to import, e.g. ``os.path`` or
-        ``os.path.join``.
-    :param alternative: The value to return if no module can be imported.
-        Defaults to None.
-    :param error_callback: If non-None, a callable that is passed the ImportError
-        when the module cannot be loaded.
-    """
-    module_segments = name.split('.')
-    last_error = None
-    while module_segments:
-        module_name = '.'.join(module_segments)
-        try:
-            module = __import__(module_name)
-        except ImportError:
-            last_error = sys.exc_info()[1]
-            module_segments.pop()
-            continue
-        else:
-            break
-    else:
-        if last_error is not None and error_callback is not None:
-            error_callback(last_error)
-        return alternative
-    nonexistent = object()
-    for segment in name.split('.')[1:]:
-        module = getattr(module, segment, nonexistent)
-        if module is nonexistent:
-            if last_error is not None and error_callback is not None:
-                error_callback(last_error)
-            return alternative
-    return module
-
-
-_RAISE_EXCEPTION = object()
-def try_imports(module_names, alternative=_RAISE_EXCEPTION, error_callback=None):
-    """Attempt to import modules.
-
-    Tries to import the first module in ``module_names``.  If it can be
-    imported, we return it.  If not, we go on to the second module and try
-    that.  The process continues until we run out of modules to try.  If none
-    of the modules can be imported, either raise an exception or return the
-    provided ``alternative`` value.
-
-    :param module_names: A sequence of module names to try to import.
-    :param alternative: The value to return if no module can be imported.
-        If unspecified, we raise an ImportError.
-    :param error_callback: If None, called with the ImportError for *each*
-        module that fails to load.
-    :raises ImportError: If none of the modules can be imported and no
-        alternative value was specified.
-    """
-    module_names = list(module_names)
-    for module_name in module_names:
-        module = try_import(module_name, error_callback=error_callback)
-        if module:
-            return module
-    if alternative is _RAISE_EXCEPTION:
-        raise ImportError(
-            "Could not import any of: %s" % ', '.join(module_names))
-    return alternative
-
-
-def safe_hasattr(obj, attr, _marker=object()):
-    """Does 'obj' have an attribute 'attr'?
-
-    Use this rather than built-in hasattr, as the built-in swallows exceptions
-    in some versions of Python and behaves unpredictably with respect to
-    properties.
-    """
-    return getattr(obj, attr, _marker) is not _marker
-
-
 def map_values(function, dictionary):
     """Map ``function`` across the values of ``dictionary``.
 

=== modified file 'testtools/testcase.py'
--- testtools/testcase.py	2012-04-03 07:39:43 +0000
+++ testtools/testcase.py	2013-01-17 09:28:19 +0000
@@ -20,9 +20,10 @@
 import types
 import unittest
 
+from extras import try_import
+
 from testtools import (
     content,
-    try_import,
     )
 from testtools.compat import (
     advance_iterator,

=== modified file 'testtools/testresult/real.py'
--- testtools/testresult/real.py	2012-12-18 09:43:43 +0000
+++ testtools/testresult/real.py	2013-01-17 09:28:19 +0000
@@ -16,12 +16,13 @@
 import sys
 import unittest
 
+from extras import safe_hasattr
+
 from testtools.compat import all, str_is_unicode, _u
 from testtools.content import (
     text_content,
     TracebackContent,
     )
-from testtools.helpers import safe_hasattr
 from testtools.tags import TagContext
 
 # From http://docs.python.org/library/datetime.html

=== modified file 'testtools/tests/helpers.py'
--- testtools/tests/helpers.py	2012-12-15 13:02:26 +0000
+++ testtools/tests/helpers.py	2013-01-17 09:28:19 +0000
@@ -8,10 +8,9 @@
 
 import sys
 
+from extras import safe_hasattr
+
 from testtools import TestResult
-from testtools.helpers import (
-    safe_hasattr,
-    )
 from testtools.content import TracebackContent
 from testtools import runtest
 

=== modified file 'testtools/tests/test_deferredruntest.py'
--- testtools/tests/test_deferredruntest.py	2012-02-06 16:02:02 +0000
+++ testtools/tests/test_deferredruntest.py	2013-01-17 09:28:19 +0000
@@ -5,6 +5,8 @@
 import os
 import signal
 
+from extras import try_import
+
 from testtools import (
     skipIf,
     TestCase,
@@ -13,7 +15,6 @@
 from testtools.content import (
     text_content,
     )
-from testtools.helpers import try_import
 from testtools.matchers import (
     Equals,
     KeysEqual,

=== modified file 'testtools/tests/test_distutilscmd.py'
--- testtools/tests/test_distutilscmd.py	2012-12-17 00:57:44 +0000
+++ testtools/tests/test_distutilscmd.py	2013-01-17 09:28:19 +0000
@@ -4,12 +4,13 @@
 
 from distutils.dist import Distribution
 
+from extras import try_import
+
 from testtools.compat import (
     _b,
     _u,
     BytesIO,
     )
-from testtools.helpers import try_import
 fixtures = try_import('fixtures')
 
 import testtools

=== modified file 'testtools/tests/test_fixturesupport.py'
--- testtools/tests/test_fixturesupport.py	2012-02-06 16:02:02 +0000
+++ testtools/tests/test_fixturesupport.py	2013-01-17 09:28:19 +0000
@@ -2,13 +2,14 @@
 
 import unittest
 
+from extras import try_import
+
 from testtools import (
     TestCase,
     content,
     content_type,
     )
 from testtools.compat import _b, _u
-from testtools.helpers import try_import
 from testtools.testresult.doubles import (
     ExtendedTestResult,
     )

=== modified file 'testtools/tests/test_helpers.py'
--- testtools/tests/test_helpers.py	2012-02-04 16:44:10 +0000
+++ testtools/tests/test_helpers.py	2013-01-17 09:28:19 +0000
@@ -1,196 +1,13 @@
 # Copyright (c) 2010-2012 testtools developers. See LICENSE for details.
 
 from testtools import TestCase
-from testtools.helpers import (
-    try_import,
-    try_imports,
-    )
-from testtools.matchers import (
-    Equals,
-    Is,
-    Not,
-    )
 from testtools.tests.helpers import (
     FullStackRunTest,
     hide_testtools_stack,
     is_stack_hidden,
-    safe_hasattr,
     )
 
 
-def check_error_callback(test, function, arg, expected_error_count,
-    expect_result):
-    """General test template for error_callback argument.
-
-    :param test: Test case instance.
-    :param function: Either try_import or try_imports.
-    :param arg: Name or names to import.
-    :param expected_error_count: Expected number of calls to the callback.
-    :param expect_result: Boolean for whether a module should
-        ultimately be returned or not.
-    """
-    cb_calls = []
-    def cb(e):
-        test.assertIsInstance(e, ImportError)
-        cb_calls.append(e)
-    try:
-        result = function(arg, error_callback=cb)
-    except ImportError:
-        test.assertFalse(expect_result)
-    else:
-        if expect_result:
-            test.assertThat(result, Not(Is(None)))
-        else:
-            test.assertThat(result, Is(None))
-    test.assertEquals(len(cb_calls), expected_error_count)
-
-
-class TestSafeHasattr(TestCase):
-
-    def test_attribute_not_there(self):
-        class Foo(object):
-            pass
-        self.assertEqual(False, safe_hasattr(Foo(), 'anything'))
-
-    def test_attribute_there(self):
-        class Foo(object):
-            pass
-        foo = Foo()
-        foo.attribute = None
-        self.assertEqual(True, safe_hasattr(foo, 'attribute'))
-
-    def test_property_there(self):
-        class Foo(object):
-            @property
-            def attribute(self):
-                return None
-        foo = Foo()
-        self.assertEqual(True, safe_hasattr(foo, 'attribute'))
-
-    def test_property_raises(self):
-        class Foo(object):
-            @property
-            def attribute(self):
-                1/0
-        foo = Foo()
-        self.assertRaises(ZeroDivisionError, safe_hasattr, foo, 'attribute')
-
-
-class TestTryImport(TestCase):
-
-    def test_doesnt_exist(self):
-        # try_import('thing', foo) returns foo if 'thing' doesn't exist.
-        marker = object()
-        result = try_import('doesntexist', marker)
-        self.assertThat(result, Is(marker))
-
-    def test_None_is_default_alternative(self):
-        # try_import('thing') returns None if 'thing' doesn't exist.
-        result = try_import('doesntexist')
-        self.assertThat(result, Is(None))
-
-    def test_existing_module(self):
-        # try_import('thing', foo) imports 'thing' and returns it if it's a
-        # module that exists.
-        result = try_import('os', object())
-        import os
-        self.assertThat(result, Is(os))
-
-    def test_existing_submodule(self):
-        # try_import('thing.another', foo) imports 'thing' and returns it if
-        # it's a module that exists.
-        result = try_import('os.path', object())
-        import os
-        self.assertThat(result, Is(os.path))
-
-    def test_nonexistent_submodule(self):
-        # try_import('thing.another', foo) imports 'thing' and returns foo if
-        # 'another' doesn't exist.
-        marker = object()
-        result = try_import('os.doesntexist', marker)
-        self.assertThat(result, Is(marker))
-
-    def test_object_from_module(self):
-        # try_import('thing.object') imports 'thing' and returns
-        # 'thing.object' if 'thing' is a module and 'object' is not.
-        result = try_import('os.path.join')
-        import os
-        self.assertThat(result, Is(os.path.join))
-
-    def test_error_callback(self):
-        # the error callback is called on failures.
-        check_error_callback(self, try_import, 'doesntexist', 1, False)
-
-    def test_error_callback_missing_module_member(self):
-        # the error callback is called on failures to find an object
-        # inside an existing module.
-        check_error_callback(self, try_import, 'os.nonexistent', 1, False)
-
-    def test_error_callback_not_on_success(self):
-        # the error callback is not called on success.
-        check_error_callback(self, try_import, 'os.path', 0, True)
-
-
-class TestTryImports(TestCase):
-
-    def test_doesnt_exist(self):
-        # try_imports('thing', foo) returns foo if 'thing' doesn't exist.
-        marker = object()
-        result = try_imports(['doesntexist'], marker)
-        self.assertThat(result, Is(marker))
-
-    def test_fallback(self):
-        result = try_imports(['doesntexist', 'os'])
-        import os
-        self.assertThat(result, Is(os))
-
-    def test_None_is_default_alternative(self):
-        # try_imports('thing') returns None if 'thing' doesn't exist.
-        e = self.assertRaises(
-            ImportError, try_imports, ['doesntexist', 'noreally'])
-        self.assertThat(
-            str(e),
-            Equals("Could not import any of: doesntexist, noreally"))
-
-    def test_existing_module(self):
-        # try_imports('thing', foo) imports 'thing' and returns it if it's a
-        # module that exists.
-        result = try_imports(['os'], object())
-        import os
-        self.assertThat(result, Is(os))
-
-    def test_existing_submodule(self):
-        # try_imports('thing.another', foo) imports 'thing' and returns it if
-        # it's a module that exists.
-        result = try_imports(['os.path'], object())
-        import os
-        self.assertThat(result, Is(os.path))
-
-    def test_nonexistent_submodule(self):
-        # try_imports('thing.another', foo) imports 'thing' and returns foo if
-        # 'another' doesn't exist.
-        marker = object()
-        result = try_imports(['os.doesntexist'], marker)
-        self.assertThat(result, Is(marker))
-
-    def test_fallback_submodule(self):
-        result = try_imports(['os.doesntexist', 'os.path'])
-        import os
-        self.assertThat(result, Is(os.path))
-
-    def test_error_callback(self):
-        # One error for every class that doesn't exist.
-        check_error_callback(self, try_imports,
-            ['os.doesntexist', 'os.notthiseither'],
-            2, False)
-        check_error_callback(self, try_imports,
-            ['os.doesntexist', 'os.notthiseither', 'os'],
-            2, True)
-        check_error_callback(self, try_imports,
-            ['os.path'],
-            0, True)
-
-
 class TestStackHiding(TestCase):
 
     run_tests_with = FullStackRunTest

=== modified file 'testtools/tests/test_run.py'
--- testtools/tests/test_run.py	2012-12-18 09:03:19 +0000
+++ testtools/tests/test_run.py	2013-01-17 09:28:19 +0000
@@ -4,11 +4,12 @@
 
 from unittest import TestSuite
 
+from extras import try_import
+
 from testtools.compat import (
     _b,
     StringIO,
     )
-from testtools.helpers import try_import
 fixtures = try_import('fixtures')
 
 import testtools

=== modified file 'testtools/tests/test_spinner.py'
--- testtools/tests/test_spinner.py	2011-01-22 17:56:00 +0000
+++ testtools/tests/test_spinner.py	2013-01-17 09:28:19 +0000
@@ -5,11 +5,12 @@
 import os
 import signal
 
+from extras import try_import
+
 from testtools import (
     skipIf,
     TestCase,
     )
-from testtools.helpers import try_import
 from testtools.matchers import (
     Equals,
     Is,

=== modified file 'testtools/tests/test_testresult.py'
--- testtools/tests/test_testresult.py	2012-12-15 13:02:26 +0000
+++ testtools/tests/test_testresult.py	2013-01-17 09:28:19 +0000
@@ -15,6 +15,8 @@
 from unittest import TestSuite
 import warnings
 
+from extras import safe_hasattr
+
 from testtools import (
     ExtendedToOriginalDecorator,
     MultiTestResult,
@@ -44,7 +46,6 @@
     TracebackContent,
     )
 from testtools.content_type import ContentType, UTF8_TEXT
-from testtools.helpers import safe_hasattr
 from testtools.matchers import (
     Contains,
     DocTestMatches,

=== modified file 'testtools/tests/test_testsuite.py'
--- testtools/tests/test_testsuite.py	2012-12-18 09:03:19 +0000
+++ testtools/tests/test_testsuite.py	2013-01-17 09:28:19 +0000
@@ -6,13 +6,14 @@
 
 import unittest
 
+from extras import try_import
+
 from testtools import (
     ConcurrentTestSuite,
     iterate_tests,
     PlaceHolder,
     TestCase,
     )
-from testtools.helpers import try_import
 from testtools.testsuite import FixtureSuite, iterate_tests, sorted_tests
 from testtools.tests.helpers import LoggingResult
 

=== modified file 'testtools/testsuite.py'
--- testtools/testsuite.py	2012-12-18 10:19:38 +0000
+++ testtools/testsuite.py	2013-01-17 09:28:19 +0000
@@ -9,13 +9,13 @@
   'sorted_tests',
   ]
 
-from testtools.helpers import safe_hasattr, try_imports
-
-Queue = try_imports(['Queue.Queue', 'queue.Queue'])
-
 import threading
 import unittest
 
+from extras import safe_hasattr, try_imports
+
+Queue = try_imports(['Queue.Queue', 'queue.Queue'])
+
 import testtools
 
 


Follow ups