Code-Apprentice
Code-Apprentice

Reputation: 83557

Debug generators and iterators

How do I debug code which uses generators and iterators? I found that adding a for loop with print statements consumes the generator/iterator and therefore breaks the rest of the code. Is it possible to inspect the "contents" of a generator/iterator without consuming the elements?

More specifically, I have something like this:

result = map(func, x)

Now I want to see what result is. I also want to see the value returned by applying a function to each element in result. In my actual code, I am getting the element in result which gives the minimum value of this function:

best = min(result, key=my_key)

Now min() is very handy, but I am getting incorrect behavior and need to figure out why. What are some tools I can use to debug something like this?

p.s. I am using PyCharm. I am pretty comfortable with the interactive debugger but still cannot figure out how to view everything that is going on here.

Upvotes: 7

Views: 4911

Answers (1)

If you want to print the values of the function arguments, and the values returned, you could use a wrapper that wraps the original function and prints the values passed into the func or my_key, and their return values. Something like

def debug_func(func):
    @functools.wraps(func)
    def wrapper(*a, **kw):
        print('Arguments', a, kw)
        rv = func(*a, **kw)
        print('Return value', repr(rv))
        return rv
    return wrapper

An usage example:

>>> list(map(debug_func(len), ['foo', 'bar', 'foobar']))
Arguments ('foo',) {}
Return value 3
Arguments ('bar',) {}
Return value 3
Arguments ('foobar',) {}
Return value 6
[3, 3, 6]

Or

>>> min(['foo', 'bar', 'foobar'], keydebug_func(len))
Arguments ('foo',) {}
Return value 3
Arguments ('bar',) {}
Return value 3
Arguments ('foobar',) {}
Return value 6
'foo'

Similar approach could be used for iterators too:

def debug_iter(iterator):
    while True:
         value = next(iterator)
         print('Iterator yielded', repr(value))
         yield value

Usage:

>>> [i for i in debug_iter(i ** 2 for i in range(5)) if i % 2]
Iterator yielded 0
Iterator yielded 1
Iterator yielded 4
Iterator yielded 9
Iterator yielded 16
[1, 9]

Upvotes: 3

Related Questions