def _set_dirty(self, node_state, flags, _depth):
        """
        Updates the dirty flags on this node.

        Marking a node as dirty automatically marks all nodes
        dependent on this node as dirty.
        """
        node = cython.declare(MDFNode)
        caller = cython.declare(MDFNode)
        obj = cython.declare(NodeSetDirtyState)
        to_process = cython.declare(cqueue)

        # start off with a reasonable amount of space and just one entry
        to_process = node_state.set_dirty_queue
        cqueue_clear(to_process)
        
        # NodeSetDirtyState objects are used instead of a tuple to avoid having to
        # keep converting native types to python types
        cqueue_push(to_process, create_NodeSetDirtyState(_depth, node_state, self, flags))

        depth = cython.declare(int, _depth)
        while cqueue_len(to_process) > 0:
            # pop the current item of the left of the queue
            obj = cqueue_popleft(to_process)
            depth, node_state, node, flags = obj.depth, obj.node_state, obj.node, obj.flags

            # if propagating dirty flags from one node to the calling nodes
            # mask the propagated dirty flags by the calling node's mask
            # as it may want to ignore some flags (this also stops them being
            # propagated further up the graph).
            if depth > 0:
                flags = flags & node._dirty_flags_propagate_mask

            # do nothing if all the dirty flags are already set
            if (node_state.dirty_flags & flags) == flags:
                if _trace_enabled:
                    ctx = _get_context(node_state.ctx_id)
                    _logger.info("%s%s[%s] already dirty (%s)" % (
                                    ("-" * depth) + "> " if depth else "",
                                    node.name,
                                    ctx,
                                    DIRTY_FLAGS.to_string(node_state.dirty_flags)))
                continue

            if _trace_enabled:
                ctx = _get_context(node_state.ctx_id)
                _logger.info("%s%s[%s] marked dirty (%s)" % (
                                ("-" * depth) + "> " if depth else "",
                                node.name,
                                ctx,
                                DIRTY_FLAGS.to_string(flags)))

            # update the dirty flag
            node_state.dirty_flags |= flags
            if node._has_on_dirty_callback:
                ctx = _get_context(node_state.ctx_id)
                node.on_set_dirty(ctx, flags)

            # remove any cached value as it will have to be re-calculated
            if flags & ~DIRTY_FLAGS_TIME:
                node_state.has_value = False
                node_state.value = None

            # add this node's callers to the list to process
            for ctx_id, callers in node_state.callers.iteritems():
                for caller in callers:
                    try:
                        caller_state = caller._states[ctx_id]
                        cqueue_push(to_process, create_NodeSetDirtyState(depth + 1, caller_state, caller, flags))
                    except __HOLE__:
                        continue
dataset/ETHPy150Open manahl/mdf/mdf/nodes.py/MDFNode._set_dirty