message.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 Support library for Python autogenerated message files. This defines
00035 the Message base class used by genpy as well as support
00036 libraries for type checking and retrieving message classes by type
00037 name.
00038 """
00039 
00040 import math
00041 import itertools
00042 import struct
00043 import sys
00044 
00045 import genmsg
00046 
00047 from base import is_simple
00048 from rostime import Time, Duration, TVal
00049 
00050 # common struct pattern singletons for msgs to use. Although this
00051 # would better placed in a generator-specific module, we don't want to
00052 # add another import to messages (which incurs higher import cost)
00053 
00054 if sys.version > '3':
00055     long = int
00056 
00057 struct_I = struct.Struct('<I')
00058 
00059 def isstring(s):
00060     """Small helper version to check an object is a string in a way that works
00061     for both Python 2 and 3
00062     """
00063     try:
00064         return isinstance(s, basestring)
00065     except NameError:
00066         return isinstance(s, str)
00067 
00068 class MessageException(Exception):
00069     """
00070     Base exception type for errors in genmsg routines
00071     """
00072     pass
00073 
00074 class DeserializationError(MessageException):
00075     """Message deserialization error"""
00076     pass
00077 
00078 class SerializationError(MessageException):
00079     """Message serialization error"""
00080     pass
00081 
00082 # we expose the generic message-strify routine for fn-oriented code like rostopic
00083 
00084 def strify_message(val, indent='', time_offset=None, current_time=None, field_filter=None, fixed_numeric_width=None):
00085     """
00086     Convert value to string representation
00087     :param val: to convert to string representation. Most likely a Message.  ``Value``
00088     :param indent: indentation. If indent is set, then the return value will have a leading \n, ``str``
00089     :param time_offset: if not None, time fields will be displayed
00090       as deltas from  time_offset, ``Time``
00091 
00092     :param current_time: currently not used. Only provided for API
00093       compatibility. current_time passes in the current time with
00094       respect to the message, ``Time``
00095     :param field_filter: filter the fields that are strified for Messages, ``fn(Message)->iter(str)``
00096     :returns: string (YAML) representation of message, ``str``
00097     """
00098 
00099     type_ = type(val)
00100     if type_ in (int, long, float) and fixed_numeric_width is not None:
00101         if type_ is float:
00102             num_str = ('%.' + str(fixed_numeric_width) + 'f') % val
00103             return num_str[:max(num_str.find('.'), fixed_numeric_width)]
00104         else:
00105             return ('%' + str(fixed_numeric_width) + 'd') % val
00106     elif type_ in (int, long, float, bool):
00107         return str(val)
00108     elif isstring(val):
00109         #TODO: need to escape strings correctly
00110         if not val:
00111             return "''"
00112         return val
00113     elif isinstance(val, TVal):
00114 
00115         if time_offset is not None and isinstance(val, Time):
00116             val = val-time_offset
00117 
00118         if fixed_numeric_width is not None:
00119             format_str = '%' + str(fixed_numeric_width) + 'd'
00120             sec_str = '\n%ssecs: ' % indent + (format_str % val.secs)
00121             nsec_str = '\n%snsecs: ' % indent + (format_str % val.nsecs)
00122             return sec_str + nsec_str
00123         else:
00124             return '\n%ssecs: %s\n%snsecs: %s'%(indent, val.secs, indent, val.nsecs)
00125 
00126     elif type_ in (list, tuple):
00127         if len(val) == 0:
00128             return "[]"
00129         val0 = val[0]
00130         if type(val0) in (int, float) and fixed_numeric_width is not None:
00131             list_str = '[' + ''.join(strify_message(v, indent, time_offset, current_time, field_filter, fixed_numeric_width) + ', ' for v in val).rstrip(', ') + ']'
00132             return list_str
00133         elif type(val0) in (int, float, str, bool):
00134             # TODO: escape strings properly
00135             return str(list(val))
00136         else:
00137             pref = indent + '- '
00138             indent = indent + '  '
00139             return '\n'+'\n'.join([pref+strify_message(v, indent, time_offset, current_time, field_filter, fixed_numeric_width) for v in val])
00140     elif isinstance(val, Message):
00141         # allow caller to select which fields of message are strified
00142         if field_filter is not None:
00143             fields = list(field_filter(val))
00144         else:
00145             fields = val.__slots__
00146 
00147         p = '%s%%s: %%s'%(indent)
00148         ni = '  '+indent
00149         if sys.hexversion > 0x03000000: #Python3
00150             vals = '\n'.join([p%(f,
00151                                  strify_message(_convert_getattr(val, f, t), ni, time_offset, current_time, field_filter, fixed_numeric_width)) for f,t in zip(val.__slots__, val._slot_types) if f in fields])
00152         else: #Python2
00153             vals = '\n'.join([p%(f,
00154                                  strify_message(_convert_getattr(val, f, t), ni, time_offset, current_time, field_filter, fixed_numeric_width)) for f,t in itertools.izip(val.__slots__, val._slot_types) if f in fields])
00155         if indent:
00156             return '\n'+vals
00157         else:
00158             return vals
00159 
00160     else:
00161         return str(val) #punt
00162 
00163 def _convert_getattr(val, f, t):
00164     """
00165     Convert atttribute types on the fly, if necessary.  This is mainly
00166     to convert uint8[] fields back to an array type.
00167     """
00168     attr = getattr(val, f)
00169     if isstring(attr) and 'uint8[' in t:
00170         return [ord(x) for x in attr]
00171     else:
00172         return attr
00173 
00174 # check_type mildly violates some abstraction boundaries between .msg
00175 # representation and the python Message representation. The
00176 # alternative is to have the message generator map .msg types to
00177 # python types beforehand, but that would make it harder to do
00178 # width/signed checks.
00179 
00180 _widths = {
00181     'byte': 8, 'char': 8, 'int8': 8, 'uint8': 8,
00182     'int16': 16, 'uint16': 16,
00183     'int32': 32, 'uint32': 32,
00184     'int64': 64, 'uint64': 64,
00185 }
00186 
00187 def check_type(field_name, field_type, field_val):
00188     """
00189     Dynamic type checker that maps ROS .msg types to python types and
00190     verifies the python value.  check_type() is not designed to be
00191     fast and is targeted at error diagnosis. This type checker is not
00192     designed to run fast and is meant only for error diagnosis.
00193 
00194     :param field_name: ROS .msg field name, ``str``
00195     :param field_type: ROS .msg field type, ``str``
00196     :param field_val: field value, ``Any``
00197     :raises: :exc:`SerializationError` If typecheck fails
00198     """
00199     if is_simple(field_type):
00200         # check sign and width
00201         if field_type in ['byte', 'int8', 'int16', 'int32', 'int64']:
00202             if type(field_val) not in [long, int]:
00203                 raise SerializationError('field %s must be an integer type'%field_name)
00204             maxval = int(math.pow(2, _widths[field_type]-1))
00205             if field_val >= maxval or field_val <= -maxval:
00206                 raise SerializationError('field %s exceeds specified width [%s]'%(field_name, field_type))
00207         elif field_type in ['char', 'uint8', 'uint16', 'uint32', 'uint64']:
00208             if type(field_val) not in [long, int] or field_val < 0:
00209                 raise SerializationError('field %s must be unsigned integer type'%field_name)
00210             maxval = int(math.pow(2, _widths[field_type]))
00211             if field_val >= maxval:
00212                 raise SerializationError('field %s exceeds specified width [%s]'%(field_name, field_type))
00213         elif field_type == 'bool':
00214             if field_val not in [True, False, 0, 1]:
00215                 raise SerializationError('field %s is not a bool'%(field_name))
00216     elif field_type == 'string':
00217         if sys.hexversion > 0x03000000:
00218             if type(field_val) == str:
00219                 try:
00220                     field_val.encode('ascii')
00221                 except UnicodeEncodeError:
00222                     raise SerializationError('field %s is a non-ascii string'%field_name)
00223             elif not type(field_val) == bytes:
00224                 raise SerializationError('field %s must be of type bytes or an ascii string'%field_name)
00225         else:
00226             if type(field_val) == unicode:
00227                 raise SerializationError('field %s is a unicode string instead of an ascii string'%field_name)
00228             elif not isstring(field_val):
00229                 raise SerializationError('field %s must be of type str'%field_name)
00230     elif field_type == 'time':
00231         if not isinstance(field_val, Time):
00232             raise SerializationError('field %s must be of type Time'%field_name)
00233     elif field_type == 'duration':
00234         if not isinstance(field_val, Duration):
00235             raise SerializationError('field %s must be of type Duration'%field_name)
00236 
00237     elif field_type.endswith(']'): # array type
00238         # use index to generate error if '[' not present
00239         base_type = field_type[:field_type.index('[')]
00240 
00241         if type(field_val) == str:
00242             if not base_type in ['char', 'uint8']:
00243                 raise SerializationError('field %s must be a list or tuple type. Only uint8[] can be a string' % field_name);
00244             else:
00245                 #It's a string so its already in byte format and we
00246                 #don't need to check the individual bytes in the
00247                 #string.
00248                 return
00249 
00250         if not type(field_val) in [list, tuple]:
00251             raise SerializationError('field %s must be a list or tuple type'%field_name)
00252         for v in field_val:
00253             check_type(field_name+"[]", base_type, v)
00254     else:
00255         if isinstance(field_val, Message):
00256             # roslib/Header is the old location of Header. We check it for backwards compat
00257             if field_val._type in ['std_msgs/Header', 'roslib/Header']:
00258                 if field_type not in ['Header', 'std_msgs/Header', 'roslib/Header']:
00259                     raise SerializationError("field %s must be a Header instead of a %s"%(field_name, field_val._type))
00260             elif field_val._type != field_type:
00261                 raise SerializationError("field %s must be of type %s instead of %s"%(field_name, field_type, field_val._type))
00262             for n, t in zip(field_val.__slots__, field_val._get_types()):
00263                 check_type("%s.%s"%(field_name,n), t, getattr(field_val, n))
00264         else:
00265             raise SerializationError("field %s must be of type [%s]"%(field_name, field_type))
00266 
00267         #TODO: dynamically load message class and do instance compare
00268 
00269 class Message(object):
00270     """Base class of Message data classes auto-generated from msg files. """
00271 
00272     # slots is explicitly both for data representation and
00273     # performance. Higher-level code assumes that there is a 1-to-1
00274     # mapping between __slots__ and message fields. In terms of
00275     # performance, explicitly settings slots eliminates dictionary for
00276     # new-style object.
00277     __slots__ = ['_connection_header']
00278 
00279     def __init__(self, *args, **kwds):
00280         """
00281         Create a new Message instance. There are multiple ways of
00282         initializing Message instances, either using a 1-to-1
00283         correspondence between constructor arguments and message
00284         fields (*args), or using Python "keyword" arguments (**kwds) to initialize named field
00285         and leave the rest with default values.
00286         """
00287         if args and kwds:
00288             raise TypeError("Message constructor may only use args OR keywords, not both")
00289         if args:
00290             if len(args) != len(self.__slots__):
00291                 raise TypeError("Invalid number of arguments, args should be %s"%str(self.__slots__)+" args are"+str(args))
00292             for i, k in enumerate(self.__slots__):
00293                 setattr(self, k, args[i])
00294         else:
00295             # validate kwds
00296             for k,v in kwds.items():
00297                 if not k in self.__slots__:
00298                     raise AttributeError("%s is not an attribute of %s"%(k, self.__class__.__name__))
00299             # iterate through slots so all fields are initialized.
00300             # this is important so that subclasses don't reference an
00301             # uninitialized field and raise an AttributeError.
00302             for k in self.__slots__:
00303                 if k in kwds:
00304                     setattr(self, k, kwds[k])
00305                 else:
00306                     setattr(self, k, None)
00307 
00308     def __getstate__(self):
00309         """
00310         support for Python pickling
00311         """
00312         return [getattr(self, x) for x in self.__slots__]
00313 
00314     def __setstate__(self, state):
00315         """
00316         support for Python pickling
00317         """
00318         for x, val in zip(self.__slots__, state):
00319             setattr(self, x, val)
00320 
00321     def _get_types(self):
00322         raise Exception("must be overriden")
00323     def _check_types(self, exc=None):
00324         """
00325         Perform dynamic type-checking of Message fields. This is performance intensive
00326         and is meant for post-error diagnosis
00327         :param exc: underlying exception that gave cause for type check, ``Exception``
00328         :raises: exc:`genpy.SerializationError` If typecheck fails
00329         """
00330         for n, t in zip(self.__slots__, self._get_types()):
00331             check_type(n, t, getattr(self, n))
00332         if exc: # if exc is set and check_type could not diagnose, raise wrapped error
00333             raise SerializationError(str(exc))
00334 
00335     def serialize(self, buff):
00336         """
00337         Serialize data into buffer
00338         :param buff: buffer, ``StringIO``
00339         """
00340         pass
00341     def deserialize(self, str):
00342         """
00343         Deserialize data in str into this instance
00344         :param str: serialized data, ``str``
00345         """
00346         pass
00347     def __repr__(self):
00348         return strify_message(self)
00349     def __str__(self):
00350         return strify_message(self)
00351     # TODO: unit test
00352     def __eq__(self, other):
00353         if not isinstance(other, self.__class__):
00354             return False
00355         for f in self.__slots__:
00356             try:
00357                 v1 = getattr(self, f)
00358                 v2 = getattr(other, f)
00359                 if type(v1) in (list, tuple) and type(v2) in (list, tuple):
00360                     # we treat tuples and lists as equivalent
00361                     if tuple(v1) != tuple(v2):
00362                         return False
00363                 elif not v1 == v2:
00364                     return False
00365             except AttributeError:
00366                 return False
00367         return True
00368 
00369 
00370 def get_printable_message_args(msg, buff=None, prefix=''):
00371     """
00372     Get string representation of msg arguments
00373     :param msg: msg message to fill, ``Message``
00374     :param prefix: field name prefix (for verbose printing), ``str``
00375     :returns: printable representation of  msg args, ``str``
00376     """
00377     try:
00378         from cStringIO import StringIO # Python 2.x
00379         python3 = 0
00380     except ImportError:
00381         from io import BytesIO # Python 3.x
00382         python3 = 1
00383 
00384     if buff is None:
00385         if python3 == 1:
00386             buff = BytesIO()
00387         else:
00388             buff = StringIO()
00389     for f in msg.__slots__:
00390         if isinstance(getattr(msg, f), Message):
00391             get_printable_message_args(getattr(msg, f), buff=buff, prefix=(prefix+f+'.'))
00392         else:
00393             buff.write(prefix+f+' ')
00394     return buff.getvalue().rstrip()
00395 
00396 def _fill_val(msg, f, v, keys, prefix):
00397     """
00398     Subroutine of L{_fill_message_args()}. Sets a particular field on a message
00399     :param f: field name, ``str``
00400     :param v: field value
00401     :param keys: keys to use as substitute values for messages and timestamps, ``dict``
00402     :raises: exc:`MessageException`
00403     """
00404     if not f in msg.__slots__:
00405         raise MessageException("No field name [%s%s]"%(prefix, f))
00406     def_val = getattr(msg, f)
00407     if isinstance(def_val, Message) or isinstance(def_val, TVal):
00408         # check for substitution key, e.g. 'now'
00409         if type(v) == str:
00410             if v in keys:
00411                 setattr(msg, f, keys[v])
00412             else:
00413                 raise MessageException("No key named [%s]"%(v))
00414         elif isinstance(def_val, TVal) and type(v) in (int, long):
00415             #special case to handle time value represented as a single number
00416             #TODO: this is a lossy conversion
00417             if isinstance(def_val, Time):
00418                 setattr(msg, f, Time.from_sec(v/1e9))
00419             elif isinstance(def_val, Duration):
00420                 setattr(msg, f, Duration.from_sec(v/1e9))
00421             else:
00422                 raise MessageException("Cannot create time values of type [%s]"%(type(def_val)))
00423         else:
00424             _fill_message_args(def_val, v, keys, prefix=(prefix+f+'.'))
00425     elif type(def_val) == list:
00426         if not type(v) in [list, tuple]:
00427             raise MessageException("Field [%s%s] must be a list or tuple instead of: %s"%(prefix, f, type(v).__name__))
00428         # determine base_type of field by looking at _slot_types
00429         idx = msg.__slots__.index(f)
00430         t = msg._slot_types[idx]
00431         base_type, is_array, length = genmsg.msgs.parse_type(t)
00432         # - for primitives, we just directly set (we don't
00433         #   type-check. we rely on serialization type checker)
00434         if base_type in genmsg.msgs.PRIMITIVE_TYPES:
00435             # 3785
00436             if length is not None and len(v) != length:
00437                 raise MessageException("Field [%s%s] has incorrect number of elements: %s != %s"%(prefix, f, len(v), length))
00438             setattr(msg, f, v)
00439 
00440         # - for complex types, we have to iteratively append to def_val
00441         else:
00442             # 3785
00443             if length is not None and len(v) != length:
00444                 raise MessageException("Field [%s%s] has incorrect number of elements: %s != %s"%(prefix, f, len(v), length))
00445             list_msg_class = get_message_class(base_type)
00446             if list_msg_class is None:
00447                 raise MessageException("Cannot instantiate messages for field [%s%s] : cannot load class %s"%(prefix, f, base_type))
00448             del def_val[:]
00449             for el in v:
00450                 inner_msg = list_msg_class()
00451                 if isinstance(inner_msg, TVal) and type(el) in (int, long):
00452                     #special case to handle time value represented as a single number
00453                     #TODO: this is a lossy conversion
00454                     if isinstance(inner_msg, Time):
00455                         inner_msg = Time.from_sec(el/1e9)
00456                     elif isinstance(inner_msg, Duration):
00457                         inner_msg = Duration.from_sec(el/1e9)
00458                     else:
00459                         raise MessageException("Cannot create time values of type [%s]"%(type(inner_msg)))
00460                 else:
00461                     _fill_message_args(inner_msg, el, keys, prefix)
00462                 def_val.append(inner_msg)
00463     else:
00464         setattr(msg, f, v)
00465 
00466 
00467 def _fill_message_args(msg, msg_args, keys, prefix=''):
00468     """
00469     Populate message with specified args.
00470 
00471     :param msg: message to fill, ``Message``
00472     :param msg_args: list of arguments to set fields to, ``[args]``
00473     :param keys: keys to use as substitute values for messages and timestamps.  ``dict``
00474     :param prefix: field name prefix (for verbose printing), ``str``
00475     :returns: unused/leftover message arguments.  ``[args]``
00476     :raise :exc:`MessageException` If not enough message arguments to fill message
00477     :raises: :exc:`ValueError` If msg or msg_args is not of correct type
00478     """
00479     if not isinstance(msg, (Message, TVal)):
00480         raise ValueError("msg must be a Message instance: %s"%msg)
00481 
00482     if type(msg_args) == dict:
00483 
00484         #print "DICT ARGS", msg_args
00485         #print "ACTIVE SLOTS",msg.__slots__
00486 
00487         for f, v in msg_args.items():
00488             # assume that an empty key is actually an empty string
00489             if v == None:
00490                 v = ''
00491             _fill_val(msg, f, v, keys, prefix)
00492     elif type(msg_args) == list:
00493 
00494         #print "LIST ARGS", msg_args
00495         #print "ACTIVE SLOTS",msg.__slots__
00496 
00497         if len(msg_args) > len(msg.__slots__):
00498             raise MessageException("Too many arguments:\n * Given: %s\n * Expected: %s"%(msg_args, msg.__slots__))
00499         elif len(msg_args) < len(msg.__slots__):
00500             raise MessageException("Not enough arguments:\n * Given: %s\n * Expected: %s"%(msg_args, msg.__slots__))
00501 
00502         for f, v in zip(msg.__slots__, msg_args):
00503             _fill_val(msg, f, v, keys, prefix)
00504     else:
00505         raise ValueError("invalid msg_args type: %s"%str(msg_args))
00506 
00507 def fill_message_args(msg, msg_args, keys={}):
00508     """
00509     Populate message with specified args. Args are assumed to be a
00510     list of arguments from a command-line YAML parser. See
00511     http://www.ros.org/wiki/ROS/YAMLCommandLine for specification on
00512     how messages are filled.
00513 
00514     fill_message_args also takes in an optional 'keys' dictionary
00515     which contain substitute values for message and time types. These
00516     values must be of the correct instance type, i.e. a Message, Time,
00517     or Duration. In a string key is encountered with these types, the
00518     value from the keys dictionary will be used instead. This is
00519     mainly used to provide values for the 'now' timestamp.
00520 
00521     :param msg: message to fill, ``Message``
00522     :param msg_args: list of arguments to set fields to, or
00523       If None, msg_args will be made an empty list., ``[args]``
00524     :param keys: keys to use as substitute values for messages and timestamps, ``dict``
00525     :raises: :exc:`MessageException` If not enough/too many message arguments to fill message
00526     """
00527     # a list of arguments is similar to python's
00528     # *args, whereas dictionaries are like **kwds.
00529 
00530     # empty messages serialize as a None, which we make equivalent to
00531     # an empty message
00532     if msg_args is None:
00533         msg_args = []
00534 
00535     # msg_args is always a list, due to the fact it is parsed from a
00536     # command-line argument list.  We have to special-case handle a
00537     # list with a single dictionary, which has precedence over the
00538     # general list representation. We offer this precedence as there
00539     # is no other way to do kwd assignments into the outer message.
00540     if len(msg_args) == 1 and type(msg_args[0]) == dict:
00541         # according to spec, if we only get one msg_arg and it's a dictionary, we
00542         # use it directly
00543         _fill_message_args(msg, msg_args[0], keys, '')
00544     else:
00545         _fill_message_args(msg, msg_args, keys, '')
00546 
00547 def _get_message_or_service_class(type_str, message_type, reload_on_error=False):
00548     """
00549     Utility for retrieving message/service class instances. Used by
00550     get_message_class and get_service_class.
00551     :param type_str: 'msg' or 'srv', ``str``
00552     :param message_type: type name of message/service, ``str``
00553     :returns: Message/Service  for message/service type or None, ``class``
00554     :raises: :exc:`ValueError` If message_type is invalidly specified
00555     """
00556     if message_type == 'time':
00557         return Time
00558     if message_type == 'duration':
00559         return Duration
00560     ## parse package and local type name for import
00561     package, base_type = genmsg.package_resource_name(message_type)
00562     if not package:
00563         if base_type == 'Header':
00564             package = 'std_msgs'
00565         else:
00566             raise ValueError("message type is missing package name: %s"%str(message_type))
00567     pypkg = val = None
00568     try:
00569         # import the package
00570         pypkg = __import__('%s.%s' % (package, type_str))
00571     except ImportError:
00572         # try importing from dry package if available
00573         try:
00574             from roslib import load_manifest
00575             from rospkg import ResourceNotFound
00576             try:
00577                 load_manifest(package)
00578                 try:
00579                     pypkg = __import__('%s.%s' % (package, type_str))
00580                 except ImportError:
00581                     pass
00582             except ResourceNotFound:
00583                 pass
00584         except ImportError:
00585             pass
00586     if pypkg:
00587         try:
00588             val = getattr(getattr(pypkg, type_str), base_type)
00589         except AttributeError:
00590             pass
00591 
00592     # this logic is mainly to support rosh, so that a user doesn't
00593     # have to exit a shell just because a message wasn't built yet
00594     if val is None and reload_on_error:
00595         try:
00596             if pypkg:
00597                 reload(pypkg)
00598             val = getattr(getattr(pypkg, type_str), base_type)
00599         except:
00600             val = None
00601     return val
00602 
00603 ## cache for get_message_class
00604 _message_class_cache = {}
00605 
00606 def get_message_class(message_type, reload_on_error=False):
00607     """
00608     Get the message class. NOTE: this function maintains a
00609     local cache of results to improve performance.
00610     :param message_type: type name of message, ``str``
00611     :param reload_on_error: (optional). Attempt to reload the Python
00612       module if unable to load message the first time. Defaults to
00613       False. This is necessary if messages are built after the first load.
00614     :returns: Message class for message/service type, ``Message class``
00615     :raises :exc:`ValueError`: if  message_type is invalidly specified
00616     """
00617     if message_type in _message_class_cache:
00618         return _message_class_cache[message_type]
00619     cls = _get_message_or_service_class('msg', message_type, reload_on_error=reload_on_error)
00620     if cls:
00621         _message_class_cache[message_type] = cls
00622     return cls
00623 
00624 ## cache for get_service_class
00625 _service_class_cache = {}
00626 
00627 def get_service_class(service_type, reload_on_error=False):
00628     """
00629     Get the service class. NOTE: this function maintains a
00630     local cache of results to improve performance.
00631     :param service_type: type name of service, ``str``
00632     :param reload_on_error: (optional). Attempt to reload the Python
00633       module if unable to load message the first time. Defaults to
00634       False. This is necessary if messages are built after the first load.
00635     :returns: Service class for service type, ``Service class``
00636     :raises :exc:`Exception` If service_type is invalidly specified
00637     """
00638     if service_type in _service_class_cache:
00639         return _service_class_cache[service_type]
00640     cls = _get_message_or_service_class('srv', service_type, reload_on_error=reload_on_error)
00641     _service_class_cache[service_type] = cls
00642     return cls
00643 


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