def check_save_load(comp, py_dir=None, test_dir='test_dir', cleanup=True,
                    logfile=None):
    """Convenience routine to check that saving & reloading `comp` works.

    comp: Component
        The component to check.

    py_dir: string or None
        The directory in which to find local Python modules.

    test_dir: string
        Name of a scratch directory to unpack in.

    cleanup: bool
        If True, the scratch directory will be removed after the test.

    logfile: string or None
        Name of file for logging progress.

    Creates an egg in the current directory, unpacks it in `test_dir`
    via a separate process, and then loads and runs the component in
    another subprocess.  Returns the first non-zero subprocess exit code,
    or zero if everything succeeded.
    """
    assert isinstance(comp, Component)

    old_level = comp.log_level
    comp.log_level = LOG_DEBUG
    name = comp.name or get_default_name(comp, comp.parent)
    start = time.time()
    egg_info = comp.save_to_egg(name, 'CSL.1', py_dir=py_dir)
    egg_name = egg_info[0]
    elapsed = time.time() - start
    size = os.path.getsize(egg_name)
    print '\nSaved %d bytes in %.2f seconds (%.2f bytes/sec)' % \
          (size, elapsed, size/elapsed)

    orig_dir = os.getcwd()
    if os.path.exists(test_dir):
        shutil.rmtree(test_dir, onerror=onerror)
    os.mkdir(test_dir)
    os.chdir(test_dir)
    egg_path = os.path.join('..', egg_name)

    try:
        print '\nUnpacking %s in subprocess...' % egg_name
        if logfile:
            stdout = open(logfile, 'w')
        else:
            stdout = None
        stderr = subprocess.STDOUT

        python = find_python()
        print '    python:', python
        unpacker = 'unpack.py'
        out = open(unpacker, 'w')
        out.write("""\
from openmdao.main.api import Component
Component.load_from_eggfile(r'%s')
""" % egg_path)
        out.close()
        args = [python, unpacker]

        retcode = subprocess.call(args, env=os.environ,
                                  stdout=stdout, stderr=stderr)
        print '    retcode', retcode
        if retcode == 0:
            print '\nRunning in subprocess...'
            os.chdir(name)
            retcode = subprocess.call([python, name+'_loader.py'],
                                      stdout=stdout, stderr=stderr)
            print '    retcode', retcode
        if logfile:
            stdout.close()
    finally:
        os.chdir(orig_dir)
        comp.log_level = old_level
        if cleanup:
            os.remove(egg_name)
            shutil.rmtree(test_dir, onerror=onerror)

    return retcode
def check_save_load(comp,
                    py_dir=None,
                    test_dir='test_dir',
                    cleanup=True,
                    logfile=None):
    """Convenience routine to check that saving & reloading `comp` works.

    comp: Component
        The component to check.

    py_dir: string or None
        The directory in which to find local Python modules.

    test_dir: string
        Name of a scratch directory to unpack in.

    cleanup: bool
        If True, the scratch directory will be removed after the test.

    logfile: string or None
        Name of file for logging progress.

    Creates an egg in the current directory, unpacks it in `test_dir`
    via a separate process, and then loads and runs the component in
    another subprocess.  Returns the first non-zero subprocess exit code,
    or zero if everything succeeded.
    """
    assert isinstance(comp, Component)

    old_level = comp.log_level
    comp.log_level = LOG_DEBUG
    name = comp.name or get_default_name(comp, comp.parent)
    start = time.time()
    egg_info = comp.save_to_egg(name, 'CSL.1', py_dir=py_dir)
    egg_name = egg_info[0]
    elapsed = time.time() - start
    size = os.path.getsize(egg_name)
    print '\nSaved %d bytes in %.2f seconds (%.2f bytes/sec)' % \
          (size, elapsed, size / elapsed)

    orig_dir = os.getcwd()
    if os.path.exists(test_dir):
        shutil.rmtree(test_dir, onerror=onerror)
    os.mkdir(test_dir)
    os.chdir(test_dir)
    egg_path = os.path.join('..', egg_name)

    try:
        print '\nUnpacking %s in subprocess...' % egg_name
        if logfile:
            stdout = open(logfile, 'w')
        else:
            stdout = None
        stderr = subprocess.STDOUT

        python = find_python()
        print '    python:', python
        unpacker = 'unpack.py'
        out = open(unpacker, 'w')
        out.write("""\
from openmdao.main.api import Component
Component.load_from_eggfile(r'%s')
""" % egg_path)
        out.close()
        args = [python, unpacker]

        retcode = subprocess.call(args,
                                  env=os.environ,
                                  stdout=stdout,
                                  stderr=stderr)
        print '    retcode', retcode
        if retcode == 0:
            print '\nRunning in subprocess...'
            os.chdir(name)
            retcode = subprocess.call([python, name + '_loader.py'],
                                      stdout=stdout,
                                      stderr=stderr)
            print '    retcode', retcode
        if logfile:
            stdout.close()
    finally:
        os.chdir(orig_dir)
        comp.log_level = old_level
        if cleanup:
            os.remove(egg_name)
            shutil.rmtree(test_dir, onerror=onerror)

    return retcode
def _clean_graph(graph, excludes=(), scope=None, parent=None):
    """Return a cleaned version of the graph. Note that this
    should not be used for really large graphs because it 
    copies the entire graph.
    """
    # make a subgraph, creating new edge/node meta dicts later if
    # we change anything
    graph = graph.subgraph(graph.nodes_iter())

    if parent is None:
        graph.graph["title"] = "unknown"
    else:
        name = parent.get_pathname()
        if hasattr(parent, "workflow"):
            name += "._derivative_graph"
        else:
            name += "._depgraph"
        graph.graph["title"] = name

    if not excludes:
        from openmdao.main.component import Component
        from openmdao.main.driver import Driver

        excluded_vars = set()

        if scope:
            try:
                cgraph = graph.component_graph()
            except AttributeError:
                pass
            else:
                for cname in cgraph.nodes():
                    try:
                        comp = getattr(scope, cname)
                    except AttributeError:
                        pass
                    else:
                        if isinstance(comp, Component):
                            graph.remove_nodes_from([".".join([cname, n]) for n, v in comp.items(framework_var=True)])

        else:  # just exclude some framework vars from Driver and Component
            c = Component()
            d = Driver()
            excluded_vars = set([n for n, v in c.items(framework_var=True)])
            excluded_vars.update([n for n, v in d.items(framework_var=True)])
    else:
        excluded_vars = set(excludes)

    conns = graph.list_connections()

    conn_nodes = set([u.split("[", 1)[0] for u, v in conns])
    conn_nodes.update([v.split("[", 1)[0] for u, v in conns])

    nodes_to_remove = []
    for node, data in graph.nodes_iter(data=True):
        cmpname, _, nodvar = node.partition(".")
        if node in excluded_vars or nodvar in excluded_vars:
            nodes_to_remove.append(node)
        else:  # update node metadata
            newdata = data
            for meta in _excluded_node_data:
                if meta in newdata:
                    if newdata is data:
                        newdata = dict(data)  # make a copy of metadata since we're changing it
                        graph.node[node] = newdata
                    del newdata[meta]
            tt_dct = {}
            for key, val in newdata.items():
                if key not in _excluded_tooltip_data:
                    tt_dct[key] = val
                elif scope is not None and key == "pseudo":
                    if val == "objective":
                        newdata["objective"] = getattr(scope, node)._orig_src
                    elif val == "constraint":
                        newdata["constraint"] = getattr(scope, node)._orig_src
            newdata["title"] = pprint.pformat(tt_dct)

    graph.remove_nodes_from(nodes_to_remove)

    for u, v, data in graph.edges_iter(data=True):
        newdata = data
        for meta in _excluded_link_data:
            if meta in newdata:
                if newdata is data:
                    newdata = dict(data)
                    graph.edge[u][v] = newdata
                del newdata[meta]

    try:
        for i, comp in enumerate(graph.component_graph()):
            graph.node[comp]["color_idx"] = i
    except AttributeError:
        pass

    # add some extra metadata to make things easier on the
    # javascript side
    for node, data in graph.nodes_iter(data=True):
        parts = node.split(".", 1)
        if len(parts) == 1:
            data["short"] = node
        else:
            data["short"] = parts[1]
            try:
                data["color_idx"] = graph.node[parts[0]]["color_idx"]
            except KeyError:
                pass

    return graph