Repository: vispy/vispy

Issue:
Why is there no asser_in in nose.tools on py2.7?
This makes me jump through hoops when writing tests.



Discussion:
`from nose.tools import assert_in` fails

Don't know. In a different package, we wrote a wrapper and put it in the equivalent of `util/misc.py`. I'll open a PR quickly. I assume the hoops you mean are having to write `assert_true(x in y)` as opposed to `assert_in(x, y)`?

Yes. The latter gives much better feedback when it fails, in contrast to "False is not True" :)

Backporting now...


Resolving patch:
diff --git a/vispy/util/__init__.py b/vispy/util/__init__.py
--- a/vispy/util/__init__.py
+++ b/vispy/util/__init__.py
@@ -7,5 +7,6 @@
 """
 
 from .misc import (_TempDir, is_string, parse_command_line_arguments,  # noqa
-                   config, sys_info)  # noqa
+                   config, sys_info, assert_in, assert_not_in,  # noqa
+                   assert_is)  # noqa
 from ._logging import logger, set_log_level, use_log_level  # noqa
diff --git a/vispy/util/misc.py b/vispy/util/misc.py
--- a/vispy/util/misc.py
+++ b/vispy/util/misc.py
@@ -257,3 +257,60 @@ def sys_info(fname=None, overwrite=False):
         with open(fname, 'w') as fid:
             fid.write(out)
     return out
+
+
+# Adapted from Python's unittest2 (which is wrapped by nose)
+# http://docs.python.org/2/license.html
+def _safe_rep(obj, short=False):
+    """Helper for assert_* ports"""
+    try:
+        result = repr(obj)
+    except Exception:
+        result = object.__repr__(obj)
+    if not short or len(result) < 80:
+        return result
+    return result[:80] + ' [truncated]...'
+
+
+def _safe_str(obj):
+    """Helper for assert_* ports"""
+    try:
+        return str(obj)
+    except Exception:
+        return object.__str__(obj)
+
+
+def _format_msg(msg, std_msg):
+    """Helper for assert_* ports"""
+    if msg is None:
+        msg = std_msg
+    else:
+        try:
+            msg = '%s : %s' % (std_msg, msg)
+        except UnicodeDecodeError:
+            msg = '%s : %s' % (_safe_str(std_msg), _safe_str(msg))
+
+
+def assert_in(member, container, msg=None):
+    """Backport for old nose.tools"""
+    if member in container:
+        return
+    std_msg = '%s not found in %s' % (_safe_rep(member), _safe_rep(container))
+    msg = _format_msg(msg, std_msg)
+    raise AssertionError(msg)
+
+
+def assert_not_in(member, container, msg=None):
+    """Backport for old nose.tools"""
+    if member not in container:
+        return
+    std_msg = '%s found in %s' % (_safe_rep(member), _safe_rep(container))
+    msg = _format_msg(msg, std_msg)
+    raise AssertionError(msg)
+
+
+def assert_is(expr1, expr2, msg=None):
+    """Backport for old nose.tools"""
+    if expr1 is not expr2:
+        std_msg = '%s is not %s' % (_safe_rep(expr1), _safe_rep(expr2))
+        raise AssertionError(_format_msg(msg, std_msg))


Test patch:
diff --git a/vispy/app/tests/test_app.py b/vispy/app/tests/test_app.py
--- a/vispy/app/tests/test_app.py
+++ b/vispy/app/tests/test_app.py
@@ -11,6 +11,7 @@
                                 ElementBuffer)
 from vispy.gloo.shader import VertexShader, FragmentShader, ShaderError
 from vispy.gloo import _screenshot
+from vispy.util import assert_in, assert_is
 
 
 def on_nonexist(self, *args):
@@ -68,7 +69,7 @@ def _test_application(backend):
     app.process_events()
     if backend is not None:
         # "in" b/c "qt" in "PySide (qt)"
-        assert_true(backend in app.backend_name)
+        assert_in(backend, app.backend_name)
     print(app)  # test __repr__
 
     # Canvas
@@ -76,7 +77,7 @@ def _test_application(backend):
     # Use "with" statement so failures don't leave open window
     # (and test context manager behavior)
     with Canvas(title='me', app=app, show=True, position=pos) as canvas:
-        assert_true(canvas.app is app)
+        assert_is(canvas.app, app)
         assert_true(canvas.native)
         print(canvas.size >= (1, 1))
         canvas.resize(90, 90)
diff --git a/vispy/util/tests/test_logging.py b/vispy/util/tests/test_logging.py
--- a/vispy/util/tests/test_logging.py
+++ b/vispy/util/tests/test_logging.py
@@ -3,7 +3,8 @@
 import logging
 
 from vispy import app
-from vispy.util import logger, use_log_level, sys_info, _TempDir
+from vispy.util import (logger, use_log_level, sys_info, _TempDir, assert_in,
+                        assert_not_in)
 
 temp_dir = _TempDir()
 
@@ -26,14 +27,14 @@ def test_debug_logging():
         a.use()
         a.quit()
     assert_equal(len(l), 1)
-    assert_true('vispy.app.application' in l[0])
+    assert_in('vispy.app.application', l[0])
 
     with use_log_level('debug', record=True) as l:
         a = app.Application()
         a.use()
         a.quit()
     assert_equal(len(l), 1)
-    assert_true('vispy.app.application' in l[0])
+    assert_in('vispy.app.application', l[0])
 
     with use_log_level('debug', 'foo', True) as l:
         a = app.Application()
@@ -46,7 +47,7 @@ def test_debug_logging():
         a.use()
         a.quit()
     assert_equal(len(l), 1)
-    assert_true('vispy.app.application' not in l[0])
+    assert_not_in('vispy.app.application', l[0])
 
 
 def test_sys_info():