def dfs_nodes_attributes_iter(G, source=None, attribute=None, upstream=True,
    null_val=None):
    """
    Produce node attributes in a depth-first-search (DFS) traversing
    upstream and terminating each search leg when finding a node having a
    non-null attribute.

    Based on Networkx dfs source

    NOTE: resolve slopes by dfs upstream and downstream finding the closest
    nodes with trusted elevation data having the largest cumulative drainage
    area.

    """
    if source is None:
        nodes = G
    else:
        nodes = [source]
    visited = set()
    for start in nodes:
        if start in visited:
            continue
        visited.add(start)
        if upstream:
            stack = [(iter(G.pred[start]), start)]
        else:
            stack = [(iter(G.succ[start]), start)]
        while stack:
            parents, child = stack[-1]
            try:
                parent = next(parents)
                node = G.node[parent]
                if attribute in node and node[attribute] is not null_val:
                    yield parent, node
                elif upstream:
                    stack.append((iter(G.pred[parent]), parent))
                else:
                    stack.append((iter(G.succ[parent]), parent))
                if parent not in visited:
                    visited.add(parent)
            except StopIteration:
                stack.pop()
