Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's with this DeprecationWarning ("callable is None")?

I'm writing a module, and I'm using unittest to write unit tests alongside the actual code.

Sometimes (almost nondeterministically, it seems), a function like the following that doesn't explicity return a value, asserted with self.assertRaises(mymodule.MyEmptyException, myfunc()) (where self refers to the subclass of unittest.TestCase) raises a mysterious DeprecationWarning.

Here's an example of such a function:

def insertn(self, items, lidex):
    """( z y x -- z b y x )
    add a list of items to the stack at the given index"""
    iter(items)
    for idx, obj in enumerate(items):
        self.insert(lidex, obj)
        lidex += 1

Its corresponding unit test:

def test_insertn_fail(self):
    """expect failure during multiple insertion"""
    self.assertRaises(mouse16.BadInternalCallException,
        stack.insertn([8, 4, 12], 16))
    with self.assertRaises(TypeError):
        stack.insertn(8, 16)

gives (for example):

./mousetesting.py:103: DeprecationWarning: callable is None

I thought making the function have a non-None return value might fix the problem (i.e. return 0) but then I get (for example):

======================================================================
ERROR: test_insertn_fail (__main__.CoreStack)
expect failure during multiple insertion
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./mousetesting.py", line 103, in test_insertn_fail
    stack.insertn([8, 4, 12], 16))
  File "/usr/lib/python3.5/unittest/case.py", line 727, in assertRaises
    return context.handle('assertRaises', args, kwargs)
  File "/usr/lib/python3.5/unittest/case.py", line 176, in handle
    callable_obj(*args, **kwargs)
TypeError: 'int' object is not callable

----------------------------------------------------------------------

So the unittest module is trying to call the return value of a function... but doesn't complain that None also isn't callable?

I don't really know enough about the internals of Python to understand what's going on here. I'd like to avoid having my tests break in a way I don't know how to fix in the future because of a DeprecationWarning I ignored.

Must my functions (but only some of them, and not all the time?) return a (memory-wasting) closure or meaningless lambda expression to avoid this? Or should I craft a thing that detects whether a test is in progress and only then return a no-op lambda? That seems like a lot of work just to avoid a DeprecationWarning.

like image 683
cat Avatar asked Feb 09 '23 02:02

cat


1 Answers

You are using self.assertRaises() incorrectly. It has to call the test function for you, in order to catch the exception:

self.assertRaises(mouse16.BadInternalCallException,
    stack.insertn, [8, 4, 12], 16)

You were passing in the result of the stack.insertn() call (which didn't raise an exception, but returned either None or an integer instead), and that return value is not callable. An older version of the method accepted None for that argument as well, which is no longer supported, hence the deprecation warning when None is passed in versus an integer.

The better way is to use self.assertRaises() as a context manager:

with self.assertRaises(mouse16.BadInternalCallException):
    stack.insertn([8, 4, 12], 16)
like image 71
Martijn Pieters Avatar answered Feb 12 '23 10:02

Martijn Pieters