I've written the following code to do a pre-order traversal of a Python dict which may contain other dicts:
def preorder_traversal(obj):
    yield obj
    if isinstance(obj, dict):
        for k, v in obj.iteritems():
            for o in preorder_traversal(v):
                yield o
Here are a few examples of its behavior:
> list(preorder_traversal({}))
[{}]
> list(preorder_traversal({'a': 1, 'b': 2}))
[{'a': 1, 'b': 2}, 1, 2]
> list(preorder_traversal({'a': {'c': 1}, 'b': 2}))
[{'a': {'c': 1}, 'b': 2}, {'c': 1}, 1, 2]
It's possible that the tree could get very large, so I'd like to add a mechanism whereby the consumer of the pre-order traversal can abort the search on an entire subtree.
Here's the code I came up with, including a nose test case. The test fails, as described below.
class IgnoreSubtree(Exception):
    pass    
def preorder_traversal(obj):
    try:
        yield obj
    except IgnoreSubtree:
        return
    if isinstance(obj, dict):
        for k, v in obj.iteritems():
            iterator = preorder_traversal(v)
            for o in iterator:
                try:
                    yield o
                except IgnoreSubtree as e:
                    try:
                        iterator.throw(e)
                    except StopIteration:  # WHY?
                        pass
def test_ignore_subtree():
    obj = {'a': {'c': 3}, 'b': 2}
    iterator = preorder_traversal(obj)
    out = []
    for o in iterator:
        out.append(o)
        if o == {'c': 3}:
            iterator.throw(IgnoreSubtree)
    eq_([obj, {'c': 3}, 2], out)
The test fails with the following error:
AssertionError: [{'a': {'c': 3}, 'b': 2}, {'c': 3}, 2] !=
                [{'a': {'c': 3}, 'b': 2}, {'c': 3}]
i.e. the IgnoreSubtree has also aborted the iteration over k/v pairs in the top-level object, it hasn't just pruned out the {'c': 3} subtree.
What's wrong with this code? And why is StopIteration being thrown in the commented location above? Is this a reasonable way to implement subtree pruning for this function, or is there a better way to go about it?
 
     
     
     
     
    