inline.py 5.94 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

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=[],
           extra_link_args=[]):
    """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():
            props += ("typedef typename %s_prop_t::template as<%s >::type" + \
                      " %sprop_%s_t;\n") % \
                      (d,t,d[0],t.replace(" ","_").replace("::","_")\
                       .replace("<","_").replace(">","_"))

    # each term on the expansion will properly unwrap a tuple pointer value
    # to a reference with the appropriate name and type
    exp_term = """typename boost::remove_pointer<typename element<%d,Args>::type>::type& %s =
                          *get<%d>(_args);"""
    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:
        return_vals += 'return_vals["%s"] = %s;\n' % (arg, arg)

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

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

    # 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);
95
    RunAction(g, make_action(boost::make_tuple(${args}), return_val));
96
97
98
99
100
101
102
103
    // 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
104
        local_dict = call_frame.f_locals
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
    if global_dict is None:
        global_dict = call_frame.f_globals
    local_dict["self___graph"] = g.underlying_graph() # the graph interface

    # 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"])

    sys.setdlopenflags(orig_dlopen_flags) # reset dlopen to normal case to
                                          # avoid unnecessary symbol collision
Tiago Peixoto's avatar
Tiago Peixoto committed
135
    return ret_vals