dynamic.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2008, Willow Garage, Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 
00033 """
00034 dynamic generation of deserializer
00035 """
00036 
00037 from __future__ import print_function
00038 
00039 try:
00040     from cStringIO import StringIO # Python 2.x
00041 except ImportError:
00042     from io import StringIO # Python 3.x
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         # Several things we have to rewrite:
00094         # - remove any import statements
00095         py_text = py_text.replace("import %s.msg"%pkg, '')
00096         # - rewrite any references to class
00097         py_text = py_text.replace("%s.msg.%s"%(pkg, base_type), gen_name)
00098         # - class declaration
00099         py_text = py_text.replace('class %s('%base_type, 'class %s('%gen_name)
00100         # - super() references for __init__
00101         py_text = py_text.replace('super(%s,'%base_type, 'super(%s,'%gen_name)
00102     # std_msgs/Header also has to be rewritten to be a local reference
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     # REP 100: pretty gross hack to deal with the fact that we moved
00119     # Header. Header is 'special' because it can be used w/o a package
00120     # name, so the lookup rules end up failing. We are committed to
00121     # never changing std_msgs/Header, so this is generally fine.
00122     msg_cat = msg_cat.replace('roslib/Header', 'std_msgs/Header')
00123 
00124     # separate msg_cat into the core message and dependencies
00125     splits = msg_cat.split('\n'+'='*80+'\n')
00126     core_msg = splits[0]
00127     deps_msgs = splits[1:]
00128 
00129     # create MsgSpec representations of .msg text
00130     specs = { core_type: genmsg.msg_loader.load_msg_from_string(msg_context, core_msg, core_type) }
00131     # - dependencies
00132     for dep_msg in deps_msgs:
00133         # dependencies require more handling to determine type name
00134         dep_type, dep_spec = _generate_dynamic_specs(msg_context, specs, dep_msg)
00135         specs[dep_type] = dep_spec
00136 
00137     # clear the message registration table and register loaded
00138     # types. The types have to be registered globally in order for
00139     # message generation of dependents to work correctly.
00140     msg_context = genmsg.msg_loader.MsgContext.create_default()
00141     search_path = {} # no ability to dynamically load
00142     for t, spec in specs.items():
00143         msg_context.register(t, spec)
00144 
00145     # process actual MsgSpecs: we accumulate them into a single file,
00146     # rewriting the generated text as needed
00147     buff = StringIO()
00148     for t, spec in specs.items():
00149         pkg, s_type = genmsg.package_resource_name(t)
00150         # dynamically generate python message code
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     # Create a temporary directory
00157     tmp_dir = tempfile.mkdtemp(prefix='genpy_')
00158 
00159     # Afterwards, we are going to remove the directory so that the .pyc file gets cleaned up if it's still around
00160     atexit.register(shutil.rmtree, tmp_dir)
00161 
00162     # write the entire text to a file and import it (it will get deleted when tmp_dir goes - above)
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     # import our temporary file as a python module, which requires modifying sys.path
00168     sys.path.append(os.path.dirname(tmp_file.name))
00169 
00170     # - strip the prefix to turn it into the python module name
00171     try:
00172         mod = __import__(os.path.basename(tmp_file.name)[:-3])
00173     except:
00174         #TODOXXX:REMOVE
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     # finally, retrieve the message classes from the dynamic module
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 


firos
Author(s): IƱigo Gonzalez, igonzalez@ikergune.com
autogenerated on Thu Jun 6 2019 17:51:04