00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 """
00034 dynamic generation of deserializer
00035 """
00036
00037 from __future__ import print_function
00038
00039 try:
00040 from cStringIO import StringIO
00041 except ImportError:
00042 from io import StringIO
00043
00044 import atexit
00045 import os
00046 import shutil
00047 import sys
00048 import tempfile
00049
00050 import genmsg
00051 import genmsg.msg_loader
00052 from genmsg import MsgContext, MsgGenerationException
00053
00054 from generator import msg_generator
00055
00056 def _generate_dynamic_specs(msg_context, specs, dep_msg):
00057 """
00058 :param dep_msg: text of dependent .msg definition, ``str``
00059 :returns: type name, message spec, ``str, MsgSpec``
00060 :raises: MsgGenerationException If dep_msg is improperly formatted
00061 """
00062 line1 = dep_msg.find('\n')
00063 msg_line = dep_msg[:line1]
00064 if not msg_line.startswith("MSG: "):
00065 raise MsgGenerationException("invalid input to generate_dynamic: dependent type is missing 'MSG:' type declaration header")
00066 dep_type = msg_line[5:].strip()
00067 dep_pkg, dep_base_type = genmsg.package_resource_name(dep_type)
00068 dep_spec = genmsg.msg_loader.load_msg_from_string(msg_context, dep_msg[line1+1:], dep_type)
00069 return dep_type, dep_spec
00070
00071 def _gen_dyn_name(pkg, base_type):
00072 """
00073 Modify pkg/base_type name so that it can safely co-exist with
00074 statically generated files.
00075
00076 :returns: name to use for pkg/base_type for dynamically generated message class.
00077 @rtype: str
00078 """
00079 return "_%s__%s"%(pkg, base_type)
00080
00081 def _gen_dyn_modify_references(py_text, types):
00082 """
00083 Modify the generated code to rewrite names such that the code can
00084 safely co-exist with messages of the same name.
00085
00086 :param py_text: genmsg_py-generated Python source code, ``str``
00087 :returns: updated text, ``str``
00088 """
00089 for t in types:
00090 pkg, base_type = genmsg.package_resource_name(t)
00091 gen_name = _gen_dyn_name(pkg, base_type)
00092
00093
00094
00095 py_text = py_text.replace("import %s.msg"%pkg, '')
00096
00097 py_text = py_text.replace("%s.msg.%s"%(pkg, base_type), gen_name)
00098
00099 py_text = py_text.replace('class %s('%base_type, 'class %s('%gen_name)
00100
00101 py_text = py_text.replace('super(%s,'%base_type, 'super(%s,'%gen_name)
00102
00103 py_text = py_text.replace('std_msgs.msg._Header.Header', _gen_dyn_name('std_msgs', 'Header'))
00104 return py_text
00105
00106 def generate_dynamic(core_type, msg_cat):
00107 """
00108 Dymamically generate message classes from msg_cat .msg text
00109 gendeps dump. This method modifies sys.path to include a temp file
00110 directory.
00111 :param core_type str: top-level ROS message type of concatenanted .msg text
00112 :param msg_cat str: concatenation of full message text (output of gendeps --cat)
00113 :raises: MsgGenerationException If dep_msg is improperly formatted
00114 """
00115 msg_context = MsgContext.create_default()
00116 core_pkg, core_base_type = genmsg.package_resource_name(core_type)
00117
00118
00119
00120
00121
00122 msg_cat = msg_cat.replace('roslib/Header', 'std_msgs/Header')
00123
00124
00125 splits = msg_cat.split('\n'+'='*80+'\n')
00126 core_msg = splits[0]
00127 deps_msgs = splits[1:]
00128
00129
00130 specs = { core_type: genmsg.msg_loader.load_msg_from_string(msg_context, core_msg, core_type) }
00131
00132 for dep_msg in deps_msgs:
00133
00134 dep_type, dep_spec = _generate_dynamic_specs(msg_context, specs, dep_msg)
00135 specs[dep_type] = dep_spec
00136
00137
00138
00139
00140 msg_context = genmsg.msg_loader.MsgContext.create_default()
00141 search_path = {}
00142 for t, spec in specs.items():
00143 msg_context.register(t, spec)
00144
00145
00146
00147 buff = StringIO()
00148 for t, spec in specs.items():
00149 pkg, s_type = genmsg.package_resource_name(t)
00150
00151 for l in msg_generator(msg_context, spec, search_path):
00152 l = _gen_dyn_modify_references(l, list(specs.keys()))
00153 buff.write(l + '\n')
00154 full_text = buff.getvalue()
00155
00156
00157 tmp_dir = tempfile.mkdtemp(prefix='genpy_')
00158
00159
00160 atexit.register(shutil.rmtree, tmp_dir)
00161
00162
00163 tmp_file = tempfile.NamedTemporaryFile(suffix=".py",dir=tmp_dir,delete=False)
00164 tmp_file.file.write(full_text.encode())
00165 tmp_file.file.close()
00166
00167
00168 sys.path.append(os.path.dirname(tmp_file.name))
00169
00170
00171 try:
00172 mod = __import__(os.path.basename(tmp_file.name)[:-3])
00173 except:
00174
00175 with open(tmp_file.name) as f:
00176 text = f.read()
00177 with open('/tmp/foo', 'w') as f2:
00178 f2.write(text)
00179 raise
00180
00181
00182 messages = {}
00183 for t in specs.keys():
00184 pkg, s_type = genmsg.package_resource_name(t)
00185 try:
00186 messages[t] = getattr(mod, _gen_dyn_name(pkg, s_type))
00187 except AttributeError:
00188 raise MsgGenerationException("cannot retrieve message class for %s/%s: %s"%(pkg, s_type, _gen_dyn_name(pkg, s_type)))
00189
00190 return messages
00191
00192