inline.py 6.13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#! /usr/bin/env python
#
# Copyright (C) 2007 Tiago de Paula Peixoto <tiago@forked.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

Tiago Peixoto's avatar
Tiago Peixoto committed
18
import sys, string, hashlib, os.path
19 20 21 22 23 24 25 26 27
from .. import core
from .. import libgraph_tool_core

try:
    import scipy.weave
except ImportError:
    raise libgraph_tool_core.raise_error\
          ("You need to have scipy installed to use 'run_action'.")

Tiago Peixoto's avatar
Tiago Peixoto committed
28 29 30 31 32 33 34 35
prefix = None
for d in [d + "/graph_tool" for d in sys.path]:
    if os.path.exists(d):
        prefix = d
        break

inc_prefix = prefix + "/include"
cxxflags = libgraph_tool_core.mod_info().cxxflags + " -I%s" % inc_prefix
36 37

# this is the code template which defines the action function object
Tiago Peixoto's avatar
Tiago Peixoto committed
38
support_template = open(prefix + "/run_action/run_action_template.hh").read()
39 40 41 42 43 44

def inline(g, code, arg_names=[], local_dict=None,
           global_dict=None, force=0, compiler="gcc", verbose=0,
           auto_downcast=1, support_code="", libraries=[],
           library_dirs=[], extra_compile_args=[],
           runtime_library_dirs=[], extra_objects=[],
45
           extra_link_args=[], mask_ret=[]):
46 47 48 49 50 51 52 53 54 55 56 57 58
    """Compile (if necessary) and run the C++ action specified by 'code',
    using weave."""

    # we need to have different template names for each actions, to avoid
    # strange RTTI issues. We'll therefore append an md5 hash of the code (plus
    # grah_tool version string) to each action's name
    import hashlib
    code_hash = hashlib.md5(code + core.__version__).hexdigest()

    # property map types
    props = ""
    for d in ["vertex", "edge", "graph"]:
        for t in core.value_types():
59
            props += (("typedef typename %s_prop_t::template as<%s >::type" + \
60 61
                      " %sprop_%s_t;\n") % \
                      (d,t,d[0],t.replace(" ","_").replace("::","_")\
62 63
                       .replace("<","_").replace(">","_"))).\
                       replace("__","_")
64 65 66

    # each term on the expansion will properly unwrap a tuple pointer value
    # to a reference with the appropriate name and type
Tiago Peixoto's avatar
Tiago Peixoto committed
67 68
    exp_term = """typename boost::remove_pointer<typename tr1::tuple_element<%d,Args>::type>::type& %s =
                          *tr1::get<%d>(_args);"""
69 70 71 72 73 74
    arg_expansion = "\n".join([ exp_term % (i,arg_names[i],i) for i in \
                                xrange(0, len(arg_names))])

    # handle returned values
    return_vals = ""
    for arg in arg_names:
75 76
        if arg not in mask_ret:
            return_vals += 'return_vals["%s"] = %s;\n' % (arg, arg)
77 78 79

    support_template = string.Template(globals()["support_template"])

Tiago Peixoto's avatar
Tiago Peixoto committed
80 81 82 83 84
    support_code += support_template.substitute(code_hash=code_hash,
                                                property_map_types=props,
                                                arg_expansion=arg_expansion,
                                                code=code,
                                                return_vals = return_vals)
85 86 87 88 89 90 91 92 93 94 95 96

    # insert a hash value of the support_code into the code below, to force
    # recompilation when support_code (and module version) changes
    support_hash = hashlib.md5(support_code + core.__version__).hexdigest()

    # the actual inline code will just call g.RunAction() on the underlying
    # GraphInterface instance. The inline arguments will be packed into a
    # tuple of pointers.
    code = string.Template(r"""
    python::object pg(python::handle<>
                        (python::borrowed((PyObject*)(self___graph))));
    GraphInterface& g = python::extract<GraphInterface&>(pg);
97
    RunAction(g, make_action(tr1::make_tuple(${args}), return_val));
98 99 100 101 102 103 104 105
    // support code hash: ${support_hash}
    """).substitute(args=", ".join(["&%s" %a for a in arg_names]),
                    code_hash=code_hash, support_hash=support_hash)

    # we need to get the locals and globals of the _calling_ function. Thus, we
    # need to go deeper into the call stack
    call_frame = sys._getframe(1)
    if local_dict is None:
Tiago Peixoto's avatar
Tiago Peixoto committed
106
        local_dict = call_frame.f_locals
107 108
    if global_dict is None:
        global_dict = call_frame.f_globals
109
    local_dict["self___graph"] = g._Graph__graph # the graph interface
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

    # RTLD_GLOBAL needs to be set in dlopen() if we want typeinfo and
    # friends to work properly across DSO boundaries. See
    # http://gcc.gnu.org/faq.html#dso
    orig_dlopen_flags = sys.getdlopenflags()
    sys.setdlopenflags(core.RTLD_NOW|core.RTLD_GLOBAL)

    # call weave and pass all the updated kw arguments
    ret_vals = \
             scipy.weave.inline(code, ["self___graph"] + arg_names, force=force,
                                local_dict=local_dict, global_dict=global_dict,
                                compiler=compiler, verbose=verbose,
                                auto_downcast=auto_downcast,
                                support_code=support_code,
                                libraries=libraries,
                                library_dirs=sys.path + library_dirs,
                                extra_compile_args=[cxxflags] + \
                                        extra_compile_args,
                                runtime_library_dirs=runtime_library_dirs,
                                extra_objects=extra_objects,
                                extra_link_args=extra_link_args)
    # check if exception was thrown
    if ret_vals["__exception_thrown"]:
        libgraph_tool_core.raise_error(ret_vals["__exception_error"])
134 135 136
    else:
        del ret_vals["__exception_thrown"]
        del ret_vals["__exception_error"]
137 138
    sys.setdlopenflags(orig_dlopen_flags) # reset dlopen to normal case to
                                          # avoid unnecessary symbol collision
Tiago Peixoto's avatar
Tiago Peixoto committed
139
    return ret_vals