def _gen_dyn_modify_references(py_text, current_type, types):
    """
    Modify the generated code to rewrite names such that the code can
    safely co-exist with messages of the same name.
    
    :param py_text: genmsg_py-generated Python source code, ``str``
    :returns: updated text, ``str``
    """
    for t in types:
        pkg, base_type = genmsg.package_resource_name(t)
        gen_name = _gen_dyn_name(pkg, base_type)
        
        # Several things we have to rewrite:
        # - remove any import statements
        py_text = py_text.replace("import %s.msg"%pkg, '')
        # - rewrite any references to class
        py_text = re.sub("(?<!\w)%s\.msg\.%s(?!\w)"%(pkg, base_type), gen_name, py_text)

    pkg, base_type = genmsg.package_resource_name(current_type)
    gen_name = _gen_dyn_name(pkg, base_type)
    # - class declaration
    py_text = py_text.replace('class %s('%base_type, 'class %s('%gen_name)
    # - super() references for __init__
    py_text = py_text.replace('super(%s,'%base_type, 'super(%s,'%gen_name)
    # std_msgs/Header also has to be rewritten to be a local reference
    py_text = py_text.replace('std_msgs.msg._Header.Header', _gen_dyn_name('std_msgs', 'Header'))
    return py_text
Exemple #2
0
def _gen_dyn_modify_references(py_text, current_type, types):
    """
    Modify the generated code to rewrite names such that the code can safely co-exist with messages of the same name.

    :param py_text: genmsg_py-generated Python source code, ``str``
    :returns: updated text, ``str``
    """
    for t in types:
        pkg, base_type = genmsg.package_resource_name(t)
        gen_name = _gen_dyn_name(pkg, base_type)

        # Several things we have to rewrite:
        # - remove any import statements
        py_text = py_text.replace('import %s.msg' % pkg, '')
        # - rewrite any references to class
        if '%s.msg.%s' % (pkg, base_type) in py_text:
            # only call expensive re.sub if the class name is in the string
            py_text = re.sub(r'(?<!\w)%s\.msg\.%s(?!\w)' % (pkg, base_type),
                             gen_name, py_text)

    pkg, base_type = genmsg.package_resource_name(current_type)
    gen_name = _gen_dyn_name(pkg, base_type)
    # - class declaration
    py_text = py_text.replace('class %s(' % base_type, 'class %s(' % gen_name)
    # - super() references for __init__
    py_text = py_text.replace('super(%s,' % base_type, 'super(%s,' % gen_name)
    # std_msgs/Header also has to be rewritten to be a local reference
    py_text = py_text.replace('std_msgs.msg._Header.Header',
                              _gen_dyn_name('std_msgs', 'Header'))
    return py_text
Exemple #3
0
def _get_message_or_service_class(type_str,
                                  message_type,
                                  reload_on_error=False):
    ## parse package and local type name for import
    package, base_type = genmsg.package_resource_name(message_type)
    if not package:
        if base_type == 'Header':
            package = 'std_msgs'
        else:
            raise ValueError("message type is missing package name: %s" %
                             str(message_type))
    pypkg = val = None
    try:
        # bootstrap our sys.path
        roslib.launcher.load_manifest(package)
        # import the package and return the class
        pypkg = __import__('%s.%s' % (package, type_str))
        val = getattr(getattr(pypkg, type_str), base_type)
    except rospkg.ResourceNotFound:
        val = None
    except ImportError:
        val = None
    except AttributeError:
        val = None

    # this logic is mainly to support rosh, so that a user doesn't
    # have to exit a shell just because a message wasn't built yet
    if val is None and reload_on_error:
        try:
            if pypkg:
                reload(pypkg)
            val = getattr(getattr(pypkg, type_str), base_type)
        except:
            val = None
    return val
def _get_action_class_genpy(type_str, message_type, reload_on_error=False):
    """
    Taken from genpy.message._get_message_or_service_class

    Utility for retrieving message/service class instances. Used by
    get_message_class and get_service_class.
    :param type_str: 'msg' or 'srv', ``str``
    :param message_type: type name of message/service, ``str``
    :returns: Message/Service  for message/service type or None, ``class``
    :raises: :exc:`ValueError` If message_type is invalidly specified
    """
    # # parse package and local type name for import
    package, base_type = genmsg.package_resource_name(message_type)
    if not package:
        if base_type == 'Header':
            package = 'std_msgs'
        else:
            raise ValueError("message type is missing package name: %s"
                             % str(message_type))
    pypkg = val = None
    try:
        # import the package and return the class
        pypkg = __import__('%s.%s' % (package, type_str))
        val = getattr(getattr(pypkg, type_str), base_type)
    except ImportError:
        val = None
    except AttributeError:
        val = None
Exemple #5
0
def _get_action_class_genpy(type_str, message_type, reload_on_error=False):
    """
    Taken from genpy.message._get_message_or_service_class

    Utility for retrieving message/service class instances. Used by
    get_message_class and get_service_class.
    :param type_str: 'msg' or 'srv', ``str``
    :param message_type: type name of message/service, ``str``
    :returns: Message/Service  for message/service type or None, ``class``
    :raises: :exc:`ValueError` If message_type is invalidly specified
    """
    # # parse package and local type name for import
    package, base_type = genmsg.package_resource_name(message_type)
    if not package:
        if base_type == 'Header':
            package = 'std_msgs'
        else:
            raise ValueError("message type is missing package name: %s" %
                             str(message_type))
    pypkg = val = None
    try:
        # import the package and return the class
        pypkg = __import__('%s.%s' % (package, type_str))
        val = getattr(getattr(pypkg, type_str), base_type)
    except ImportError:
        val = None
    except AttributeError:
        val = None
Exemple #6
0
def _get_message_or_service_class(type_str, message_type, reload_on_error=False):
    ## parse package and local type name for import
    package, base_type = genmsg.package_resource_name(message_type)
    if not package:
        if base_type == 'Header':
            package = 'std_msgs'
        else:
            raise ValueError("message type is missing package name: %s"%str(message_type))
    pypkg = val = None
    try: 
        # bootstrap our sys.path
        roslib.launcher.load_manifest(package)
        # import the package and return the class
        pypkg = __import__('%s.%s'%(package, type_str))
        val = getattr(getattr(pypkg, type_str), base_type)
    except rospkg.ResourceNotFound:
        val = None
    except ImportError:
        val = None
    except AttributeError:
        val = None

    # this logic is mainly to support rosh, so that a user doesn't
    # have to exit a shell just because a message wasn't built yet
    if val is None and reload_on_error:
        try:
            if pypkg:
                reload(pypkg)
            val = getattr(getattr(pypkg, type_str), base_type)
        except:
            val = None
    return val
Exemple #7
0
def _get_message_or_service_class(type_str,
                                  message_type,
                                  reload_on_error=False):
    """
    Retrieve message/service class instances.

    Used by get_message_class and get_service_class.
    :param type_str: 'msg' or 'srv', ``str``
    :param message_type: type name of message/service, ``str``
    :returns: Message/Service  for message/service type or None, ``class``
    :raises: :exc:`ValueError` If message_type is invalidly specified
    """
    if message_type == 'time':
        return Time
    if message_type == 'duration':
        return Duration
    # parse package and local type name for import
    package, base_type = genmsg.package_resource_name(message_type)
    if not package:
        if base_type == 'Header':
            package = 'std_msgs'
        else:
            raise ValueError('message type is missing package name: %s' %
                             str(message_type))
    pypkg = val = None
    try:
        # import the package
        pypkg = __import__('%s.%s' % (package, type_str))
    except ImportError:
        # try importing from dry package if available
        try:
            from roslib import load_manifest
            from rospkg import ResourceNotFound
            try:
                load_manifest(package)
                try:
                    pypkg = __import__('%s.%s' % (package, type_str))
                except ImportError:
                    pass
            except ResourceNotFound:
                pass
        except ImportError:
            pass
    if pypkg:
        try:
            val = getattr(getattr(pypkg, type_str), base_type)
        except AttributeError:
            pass

    # this logic is mainly to support rosh, so that a user doesn't
    # have to exit a shell just because a message wasn't built yet
    if val is None and reload_on_error:
        try:
            if pypkg:
                reload(pypkg)
            val = getattr(getattr(pypkg, type_str), base_type)
        except Exception:
            val = None
    return val
Exemple #8
0
def _get_message_or_service_class(type_str, message_type, reload_on_error=False):
    """
    Utility for retrieving message/service class instances. Used by
    get_message_class and get_service_class. 
    :param type_str: 'msg' or 'srv', ``str``
    :param message_type: type name of message/service, ``str``
    :returns: Message/Service  for message/service type or None, ``class``
    :raises: :exc:`ValueError` If message_type is invalidly specified
    """
    if message_type == 'time':
        return Time
    if message_type == 'duration':
        return Duration
    ## parse package and local type name for import
    package, base_type = genmsg.package_resource_name(message_type)
    if not package:
        if base_type == 'Header':
            package = 'std_msgs'
        else:
            raise ValueError("message type is missing package name: %s"%str(message_type))
    pypkg = val = None
    try:
        # import the package
        pypkg = __import__('%s.%s' % (package, type_str))
    except ImportError:
        # try importing from dry package if available
        try:
            from roslib import load_manifest
            from rospkg import ResourceNotFound
            try:
                load_manifest(package)
                try:
                    pypkg = __import__('%s.%s' % (package, type_str))
                except ImportError:
                    pass
            except ResourceNotFound:
                pass
        except ImportError:
            pass
    if pypkg:
        try:
            val = getattr(getattr(pypkg, type_str), base_type)
        except AttributeError:
            pass

    # this logic is mainly to support rosh, so that a user doesn't
    # have to exit a shell just because a message wasn't built yet
    if val is None and reload_on_error:
        try:
            if pypkg:
                reload(pypkg)
            val = getattr(getattr(pypkg, type_str), base_type)
        except:
            val = None
    return val
Exemple #9
0
def _generate_dynamic_specs(msg_context, specs, dep_msg):
    """
    :param dep_msg: text of dependent .msg definition, ``str``
    :returns: type name, message spec, ``str, MsgSpec``
    :raises: MsgGenerationException If dep_msg is improperly formatted
    """
    line1 = dep_msg.find('\n')
    msg_line = dep_msg[:line1]
    if not msg_line.startswith("MSG: "):
        raise MsgGenerationException("invalid input to generate_dynamic: dependent type is missing 'MSG:' type declaration header")
    dep_type = msg_line[5:].strip()
    dep_pkg, dep_base_type = genmsg.package_resource_name(dep_type)
    dep_spec = genmsg.msg_loader.load_msg_from_string(msg_context, dep_msg[line1+1:], dep_type)
    return dep_type, dep_spec
Exemple #10
0
def _generate_dynamic_specs(msg_context, specs, dep_msg):
    """
    :param dep_msg: text of dependent .msg definition, ``str``
    :returns: type name, message spec, ``str, MsgSpec``
    :raises: MsgGenerationException If dep_msg is improperly formatted
    """
    line1 = dep_msg.find('\n')
    msg_line = dep_msg[:line1]
    if not msg_line.startswith("MSG: "):
        raise MsgGenerationException("invalid input to generate_dynamic: dependent type is missing 'MSG:' type declaration header")
    dep_type = msg_line[5:].strip()
    dep_pkg, dep_base_type = genmsg.package_resource_name(dep_type)
    dep_spec = genmsg.msg_loader.load_msg_from_string(msg_context, dep_msg[line1+1:], dep_type)
    return dep_type, dep_spec
Exemple #11
0
def test_package_resource_name():
    from genmsg import package_resource_name
    assert ('', '') == package_resource_name('')
    assert ('', 'foo') == package_resource_name('foo')
    assert ('foo', 'bar') == package_resource_name('foo/bar')
    assert ('foo', '') == package_resource_name('foo/')
    try:
        # only allowed single separator
        package_resource_name("foo/bar/baz")
        assert False, "should have raised ValueError"
    except ValueError:
        pass
def test_package_resource_name():
    from genmsg import package_resource_name
    assert ('', '') == package_resource_name('')
    assert ('', 'foo') == package_resource_name('foo')
    assert ('foo', 'bar') == package_resource_name('foo/bar')
    assert ('foo', '') == package_resource_name('foo/')
    try:
        # only allowed single separator
        package_resource_name("foo/bar/baz")
        assert False, "should have raised ValueError"
    except ValueError:
        pass
Exemple #13
0
def _get_message_or_service_class(type_str,
                                  message_type,
                                  reload_on_error=False):
    """
    Utility for retrieving message/service class instances. Used by
    get_message_class and get_service_class. 
    :param type_str: 'msg' or 'srv', ``str``
    :param message_type: type name of message/service, ``str``
    :returns: Message/Service  for message/service type or None, ``class``
    :raises: :exc:`ValueError` If message_type is invalidly specified
    """
    if message_type == 'time':
        return Time
    if message_type == 'duration':
        return Duration
    ## parse package and local type name for import
    package, base_type = genmsg.package_resource_name(message_type)
    if not package:
        if base_type == 'Header':
            package = 'std_msgs'
        else:
            raise ValueError("message type is missing package name: %s" %
                             str(message_type))
    pypkg = val = None
    try:
        # import the package and return the class
        pypkg = __import__('%s.%s' % (package, type_str))
        val = getattr(getattr(pypkg, type_str), base_type)
    except ImportError:
        val = None
    except AttributeError:
        val = None

    # this logic is mainly to support rosh, so that a user doesn't
    # have to exit a shell just because a message wasn't built yet
    if val is None and reload_on_error:
        try:
            if pypkg:
                reload(pypkg)
            val = getattr(getattr(pypkg, type_str), base_type)
        except:
            val = None
    return val
Exemple #14
0
def generate_dynamic(core_type, msg_cat):
    """
    Dymamically generate message classes from msg_cat .msg text gendeps dump.

    This method modifies sys.path to include a temp file directory.
    :param core_type str: top-level ROS message type of concatenated .msg text
    :param msg_cat str: concatenation of full message text (output of gendeps --cat)
    :raises: MsgGenerationException If dep_msg is improperly formatted
    """
    msg_context = MsgContext.create_default()
    core_pkg, core_base_type = genmsg.package_resource_name(core_type)

    # REP 100: pretty gross hack to deal with the fact that we moved
    # Header. Header is 'special' because it can be used w/o a package
    # name, so the lookup rules end up failing. We are committed to
    # never changing std_msgs/Header, so this is generally fine.
    msg_cat = msg_cat.replace('roslib/Header', 'std_msgs/Header')

    # separate msg_cat into the core message and dependencies
    splits = msg_cat.split('\n' + '=' * 80 + '\n')
    core_msg = splits[0]
    deps_msgs = splits[1:]

    # create MsgSpec representations of .msg text
    specs = {
        core_type:
        msg_loader.load_msg_from_string(msg_context, core_msg, core_type)
    }
    # - dependencies
    for dep_msg in deps_msgs:
        # dependencies require more handling to determine type name
        dep_type, dep_spec = _generate_dynamic_specs(msg_context, specs,
                                                     dep_msg)
        specs[dep_type] = dep_spec

    # clear the message registration table and register loaded
    # types. The types have to be registered globally in order for
    # message generation of dependents to work correctly.
    msg_context = msg_loader.MsgContext.create_default()
    search_path = {}  # no ability to dynamically load
    for t, spec in specs.items():
        msg_context.register(t, spec)

    # process actual MsgSpecs: we accumulate them into a single file,
    # rewriting the generated text as needed
    buff = StringIO()
    for t, spec in specs.items():
        pkg, s_type = genmsg.package_resource_name(t)
        # dynamically generate python message code
        for line in msg_generator(msg_context, spec, search_path):
            line = _gen_dyn_modify_references(line, t, list(specs.keys()))
            buff.write(line + '\n')
    full_text = buff.getvalue()

    # Create a temporary directory
    tmp_dir = tempfile.mkdtemp(prefix='genpy_')

    # Afterwards, we are going to remove the directory so that the .pyc file gets cleaned up if it's still around
    atexit.register(shutil.rmtree, tmp_dir)

    # write the entire text to a file and import it (it will get deleted when tmp_dir goes - above)
    tmp_file = tempfile.NamedTemporaryFile(mode='w',
                                           suffix='.py',
                                           dir=tmp_dir,
                                           delete=False)
    tmp_file.file.write(full_text)
    tmp_file.file.close()

    # import our temporary file as a python module, which requires modifying sys.path
    sys.path.append(os.path.dirname(tmp_file.name))

    # - strip the prefix to turn it into the python module name
    try:
        mod = __import__(os.path.basename(tmp_file.name)[:-3])
    except Exception:
        # TODOXXX:REMOVE
        with open(tmp_file.name) as f:
            text = f.read()
            with open('/tmp/foo', 'w') as f2:
                f2.write(text)
        raise

    # finally, retrieve the message classes from the dynamic module
    messages = {}
    for t in specs.keys():
        pkg, s_type = genmsg.package_resource_name(t)
        try:
            messages[t] = getattr(mod, _gen_dyn_name(pkg, s_type))
        except AttributeError:
            raise MsgGenerationException(
                'cannot retrieve message class for %s/%s: %s' %
                (pkg, s_type, _gen_dyn_name(pkg, s_type)))
        messages[t]._spec = specs[t]

    return messages
Exemple #15
0
def generate_dynamic(core_type, msg_cat):
    """
    Dymamically generate message classes from msg_cat .msg text
    gendeps dump. This method modifies sys.path to include a temp file
    directory.
    :param core_type str: top-level ROS message type of concatenanted .msg text
    :param msg_cat str: concatenation of full message text (output of gendeps --cat)
    :raises: MsgGenerationException If dep_msg is improperly formatted
    """
    msg_context = MsgContext.create_default()
    core_pkg, core_base_type = genmsg.package_resource_name(core_type)
    
    # REP 100: pretty gross hack to deal with the fact that we moved
    # Header. Header is 'special' because it can be used w/o a package
    # name, so the lookup rules end up failing. We are committed to
    # never changing std_msgs/Header, so this is generally fine.
    msg_cat = msg_cat.replace('roslib/Header', 'std_msgs/Header')

    # separate msg_cat into the core message and dependencies
    splits = msg_cat.split('\n'+'='*80+'\n')
    core_msg = splits[0]
    deps_msgs = splits[1:]

    # create MsgSpec representations of .msg text
    specs = { core_type: genmsg.msg_loader.load_msg_from_string(msg_context, core_msg, core_type) }
    # - dependencies
    for dep_msg in deps_msgs:
        # dependencies require more handling to determine type name
        dep_type, dep_spec = _generate_dynamic_specs(msg_context, specs, dep_msg)
        specs[dep_type] = dep_spec
    
    # clear the message registration table and register loaded
    # types. The types have to be registered globally in order for
    # message generation of dependents to work correctly.
    msg_context = genmsg.msg_loader.MsgContext.create_default()
    search_path = {} # no ability to dynamically load
    for t, spec in specs.items():
        msg_context.register(t, spec)

    # process actual MsgSpecs: we accumulate them into a single file,
    # rewriting the generated text as needed
    buff = StringIO()
    for t, spec in specs.items():
        pkg, s_type = genmsg.package_resource_name(t)
        # dynamically generate python message code
        for l in msg_generator(msg_context, spec, search_path):
            l = _gen_dyn_modify_references(l, list(specs.keys()))
            buff.write(l + '\n')
    full_text = buff.getvalue()

    # Create a temporary directory
    tmp_dir = tempfile.mkdtemp(prefix='genpy_')

    # Afterwards, we are going to remove the directory so that the .pyc file gets cleaned up if it's still around
    atexit.register(shutil.rmtree, tmp_dir)
    
    # write the entire text to a file and import it (it will get deleted when tmp_dir goes - above)
    tmp_file = tempfile.NamedTemporaryFile(suffix=".py",dir=tmp_dir,delete=False)
    tmp_file.file.write(full_text)
    tmp_file.file.close()

    # import our temporary file as a python module, which requires modifying sys.path
    sys.path.append(os.path.dirname(tmp_file.name))

    # - strip the prefix to turn it into the python module name
    try:
        mod = __import__(os.path.basename(tmp_file.name)[:-3])
    except:
        #TODOXXX:REMOVE
        with open(tmp_file.name) as f:
            text = f.read()
            with open('/tmp/foo', 'w') as f2:
                f2.write(text)
        raise

    # finally, retrieve the message classes from the dynamic module
    messages = {}
    for t in specs.keys():
        pkg, s_type = genmsg.package_resource_name(t)
        try:
            messages[t] = getattr(mod, _gen_dyn_name(pkg, s_type))
        except AttributeError:
            raise MsgGenerationException("cannot retrieve message class for %s/%s: %s"%(pkg, s_type, _gen_dyn_name(pkg, s_type)))

    return messages