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 Library for Python message generation.
00035
00036 The structure of the serialization descends several levels of serializers:
00037 - msg_generator: generator for an individual msg file
00038 - serialize_fn_generator: generator for msg.serialize()
00039 - serializer_generator
00040 - field-type-specific serializers
00041 raise MsgGenerationException("unknown file extension: %s"%f)
00042
00043 - deserialize_fn_generator: generator for msg.deserialize()
00044 - serializer_generator
00045 - field-type-specific serializers
00046 """
00047
00048 from __future__ import print_function
00049
00050 import os
00051 import keyword
00052 import itertools
00053 import sys
00054 import traceback
00055 import struct
00056
00057 import genmsg
00058 import genmsg.msgs
00059 import genmsg.msg_loader
00060 import genmsg.gentools
00061
00062 from genmsg import InvalidMsgSpec, MsgContext, MsgSpec, MsgGenerationException
00063 from genmsg.base import log
00064
00065 from base import is_simple, SIMPLE_TYPES, SIMPLE_TYPES_DICT
00066 from generate_numpy import unpack_numpy, pack_numpy, NUMPY_DTYPE
00067 from generate_struct import reduce_pattern, serialize, \
00068 int32_pack, int32_unpack, pack, pack2, unpack, unpack2, compute_struct_pattern, \
00069 clear_patterns, add_pattern, get_patterns
00070
00071
00072 INDENT = ' '
00073
00074 def get_registered_ex(msg_context, type_):
00075 """
00076 wrapper for get_registered that wraps unknown types with a MsgGenerationException
00077 :param type_: ROS message type, ``str``
00078 """
00079 try:
00080 return msg_context.get_registered(type_)
00081 except:
00082 raise MsgGenerationException("Unknown type [%s]. Please check that the manifest.xml correctly declares dependencies."%type_)
00083
00084
00085
00086
00087 class Special:
00088
00089 def __init__(self, constructor, post_deserialize, import_str):
00090 """
00091 :param constructor: expression to instantiate new type instance for deserialization, ``str``
00092 :param post_Deserialize: format string for expression to evaluate on type instance after deserialization is complete., ``str``
00093 variable name will be passed in as the single argument to format string.
00094 :param import_str: import to include if type is present, ``str``
00095 """
00096 self.constructor = constructor
00097 self.post_deserialize = post_deserialize
00098 self.import_str = import_str
00099
00100 def get_post_deserialize(self, varname):
00101 """
00102 :returns: Post-deserialization code to executed (unindented) or
00103 ``None`` if no post-deserialization is required, ``str``
00104 """
00105 if self.post_deserialize:
00106 return self.post_deserialize%varname
00107 else:
00108 return None
00109
00110 _SPECIAL_TYPES = {
00111 genmsg.HEADER: Special('std_msgs.msg._Header.Header()', None, 'import std_msgs.msg'),
00112 genmsg.TIME: Special('genpy.Time()', '%s.canon()', 'import genpy'),
00113 genmsg.DURATION: Special('genpy.Duration()', '%s.canon()', 'import genpy'),
00114 }
00115
00116 def is_special(type_):
00117 """
00118 :returns: ``True` if *type_* is a special type (i.e. builtin represented as a class instead of a primitive), ``bool``
00119 """
00120 return type_ in _SPECIAL_TYPES
00121
00122 def get_special(type_):
00123 """
00124 :returns: special type handler for *type_* or ``None``, ``Special``
00125 """
00126 return _SPECIAL_TYPES.get(type_, None)
00127
00128
00129
00130
00131
00132 def default_value(msg_context, field_type, default_package):
00133 """
00134 Compute default value for field_type
00135
00136 :param default_package: default package, ``str``
00137 :param field_type: ROS .msg field type, ``str``
00138 :returns: default value encoded in Python string representation, ``str``
00139 """
00140 if field_type in ['byte', 'int8', 'int16', 'int32', 'int64',\
00141 'char', 'uint8', 'uint16', 'uint32', 'uint64']:
00142 return '0'
00143 elif field_type in ['float32', 'float64']:
00144 return '0.'
00145 elif field_type == 'string':
00146
00147 return "''"
00148 elif field_type == 'bool':
00149 return 'False'
00150 elif field_type.endswith(']'):
00151 base_type, is_array, array_len = genmsg.msgs.parse_type(field_type)
00152 if base_type in ['char', 'uint8']:
00153
00154 if array_len is not None:
00155 return "chr(0)*%s"%array_len
00156 else:
00157 return "''"
00158 elif array_len is None:
00159 return '[]'
00160 else:
00161 def_val = default_value(msg_context, base_type, default_package)
00162 return '[' + ','.join(itertools.repeat(def_val, array_len)) + ']'
00163 else:
00164 return compute_constructor(msg_context, default_package, field_type)
00165
00166 def flatten(msg_context, msg):
00167 """
00168 Flattens the msg spec so that embedded message fields become
00169 direct references. The resulting MsgSpec isn't a true/legal
00170 :class:`MsgSpec` and should only be used for serializer generation.
00171 :param msg: MsgSpec to flatten
00172 :returns: flattened MsgSpec message
00173 """
00174 new_types = []
00175 new_names = []
00176 for t, n in zip(msg.types, msg.names):
00177
00178 msg_type, is_array, _ = genmsg.msgs.parse_type(t)
00179
00180 if not is_array and msg_context.is_registered(t):
00181 msg_spec = flatten(msg_context, msg_context.get_registered(t))
00182 new_types.extend(msg_spec.types)
00183 for n2 in msg_spec.names:
00184 new_names.append(n+'.'+n2)
00185 else:
00186
00187
00188 new_types.append(t)
00189 new_names.append(n)
00190 return MsgSpec(new_types, new_names, msg.constants, msg.text, msg.full_name)
00191
00192 def make_python_safe(spec):
00193 """
00194 Remap field/constant names in spec to avoid collision with Python reserved words.
00195
00196 :param spec: msg spec to map to new, python-safe field names, ``MsgSpec``
00197 :returns: python-safe message specification, ``MsgSpec``
00198 """
00199 new_c = [genmsg.Constant(c.type, _remap_reserved(c.name), c.val, c.val_text) for c in spec.constants]
00200 return MsgSpec(spec.types, [_remap_reserved(n) for n in spec.names], new_c, spec.text, spec.full_name)
00201
00202 def _remap_reserved(field_name):
00203 """
00204 Map field_name to a python-safe representation, if necessary
00205 :param field_name: msg field name, ``str``
00206 :returns: remapped name, ``str``
00207 """
00208
00209 idx = field_name.rfind('.')
00210 if idx > 0:
00211 prefix = field_name[:idx+1]
00212 sub_field_name = field_name[idx+1:]
00213 else:
00214 prefix = ''
00215 sub_field_name = field_name
00216
00217 if sub_field_name in keyword.kwlist + ['self']:
00218 sub_field_name = sub_field_name + "_"
00219 return prefix + sub_field_name
00220
00221
00222
00223
00224 def compute_post_deserialize(type_, varname):
00225 """
00226 Compute post-deserialization code for type_, if necessary
00227 :returns: code to execute post-deserialization (unindented), or None if not necessary. ``str``
00228 """
00229 s = get_special(type_)
00230 if s is not None:
00231 return s.get_post_deserialize(varname)
00232
00233 def compute_constructor(msg_context, package, type_):
00234 """
00235 Compute python constructor expression for specified message type implementation
00236 :param package str: package that type is being imported into. Used
00237 to resolve type_ if package is not specified. ``str``
00238 :param type_: message type, ``str``
00239 """
00240 if is_special(type_):
00241 return get_special(type_).constructor
00242 elif genmsg.msgs.bare_msg_type(type_) != type_:
00243
00244 return None
00245 else:
00246 base_pkg, base_type_ = compute_pkg_type(package, type_)
00247 if not msg_context.is_registered("%s/%s"%(base_pkg,base_type_)):
00248 return None
00249 else:
00250 return '%s.msg.%s()'%(base_pkg, base_type_)
00251
00252 def compute_pkg_type(package, type_):
00253 """
00254 :param package: package that type is being imported into, ``str``
00255 :param type: message type (package resource name), ``str``
00256 :returns: python package and type name, ``(str, str)``
00257 """
00258 splits = type_.split(genmsg.SEP)
00259 if len(splits) == 1:
00260 return package, splits[0]
00261 elif len(splits) == 2:
00262 return tuple(splits)
00263 else:
00264 raise MsgGenerationException("illegal message type: %s"%type_)
00265
00266 def compute_import(msg_context, package, type_):
00267 """
00268 Compute python import statement for specified message type implementation
00269 :param package: package that type is being imported into, ``str``
00270 :param type_: message type (package resource name), ``str``
00271 :returns: list of import statements (no newline) required to use type_ from package, ``[str]``
00272 """
00273
00274 orig_base_type = genmsg.msgs.bare_msg_type(type_)
00275
00276
00277
00278 pkg, base_type = compute_pkg_type(package, orig_base_type)
00279 full_msg_type = "%s/%s"%(pkg, base_type)
00280
00281
00282
00283
00284 if genmsg.msgs.is_builtin(orig_base_type) or \
00285 genmsg.msgs.is_header_type(orig_base_type):
00286
00287
00288
00289 if is_special(base_type):
00290 retval = [get_special(base_type).import_str]
00291 else:
00292 retval = []
00293 elif not msg_context.is_registered(full_msg_type):
00294 retval = []
00295 else:
00296 retval = ['import %s.msg'%pkg]
00297 iter_types = get_registered_ex(msg_context, full_msg_type).types
00298 for t in iter_types:
00299 assert t != full_msg_type, "msg [%s] has circular self-dependencies"%(full_msg_type)
00300 full_sub_type = "%s/%s"%(package, t)
00301 log("compute_import", full_msg_type, package, t)
00302 sub = compute_import(msg_context, package, t)
00303 retval.extend([x for x in sub if not x in retval])
00304 return retval
00305
00306 def compute_full_text_escaped(msg_context, spec):
00307 """
00308 Same as genmsg.compute_full_text, except that the
00309 resulting text is escaped to be safe for Python's triple-quote string
00310 quoting
00311
00312 :param get_deps_dict: dictionary returned by load_dependencies call, ``dict``
00313 :returns: concatenated text for msg/srv file and embedded msg/srv types. Text will be escaped for triple-quote, ``str``
00314 """
00315 msg_definition = genmsg.compute_full_text(msg_context, spec)
00316 msg_definition.replace('"""', r'\"\"\"')
00317 return msg_definition
00318
00319
00320
00321
00322 _serial_context = ''
00323 _context_stack = []
00324
00325 _counter = 0
00326 def next_var():
00327
00328 global _counter
00329 _counter += 1
00330 return '_v%s'%_counter
00331
00332 def reset_var():
00333 global _counter
00334 _counter = 0
00335
00336 def push_context(context):
00337 """
00338 Push new variable context onto context stack. The context stack
00339 manages field-reference context for serialization, e.g. 'self.foo'
00340 vs. 'self.bar.foo' vs. 'var.foo'
00341 """
00342 global _serial_context, _context_stack
00343 _context_stack.append(_serial_context)
00344 _serial_context = context
00345
00346 def pop_context():
00347 """
00348 Pop variable context from context stack. The context stack manages
00349 field-reference context for serialization, e.g. 'self.foo'
00350 vs. 'self.bar.foo' vs. 'var.foo'
00351 """
00352 global _serial_context
00353 _serial_context = _context_stack.pop()
00354
00355
00356
00357
00358
00359
00360
00361 def len_serializer_generator(var, is_string, serialize):
00362 """
00363 Generator for array-length serialization (32-bit, little-endian unsigned integer)
00364 :param var: variable name, ``str``
00365 :param is_string: if True, variable is a string type, ``bool``
00366 :param serialize bool: if True, generate code for
00367 serialization. Other, generate code for deserialization, ``bool``
00368 """
00369 if serialize:
00370 yield "length = len(%s)"%var
00371
00372
00373
00374
00375
00376 if not is_string:
00377 yield int32_pack("length")
00378 else:
00379 yield "start = end"
00380 yield "end += 4"
00381 yield int32_unpack('length', 'str[start:end]')
00382
00383 def string_serializer_generator(package, type_, name, serialize):
00384 """
00385 Generator for string types. similar to arrays, but with more
00386 efficient call to struct.pack.
00387
00388 :param name: spec field name, ``str``
00389 :param serialize: if ``True``, generate code for
00390 serialization. Other, generate code for deserialization, ``bool``
00391 """
00392
00393
00394 if _serial_context and serialize:
00395
00396 yield "_x = %s%s"%(_serial_context, name)
00397 var = "_x"
00398 else:
00399 var = _serial_context+name
00400
00401
00402
00403 base_type, is_array, array_len = genmsg.msgs.parse_type(type_)
00404
00405 if base_type not in ['uint8', 'char'] or array_len is None:
00406 for y in len_serializer_generator(var, True, serialize):
00407 yield y
00408
00409 if serialize:
00410
00411
00412
00413 base_type, is_array, array_len = genmsg.msgs.parse_type(type_)
00414 if base_type in ['uint8', 'char']:
00415 yield "# - if encoded as a list instead, serialize as bytes instead of string"
00416 if array_len is None:
00417 yield "if type(%s) in [list, tuple]:"%var
00418 yield INDENT+pack2("'<I%sB'%length", "length, *%s"%var)
00419 yield "else:"
00420 yield INDENT+pack2("'<I%ss'%length", "length, %s"%var)
00421 else:
00422 yield "if type(%s) in [list, tuple]:"%var
00423 yield INDENT+pack('%sB'%array_len, "*%s"%var)
00424 yield "else:"
00425 yield INDENT+pack('%ss'%array_len, var)
00426 else:
00427
00428 yield "if python3 or type(%s) == unicode:"%(var)
00429 yield INDENT+"%s = %s.encode('utf-8')"%(var,var)
00430 yield INDENT+"length = len(%s)"%(var)
00431
00432 yield "if python3:"
00433 yield INDENT+pack2("'<I%sB'%length", "length, *%s"%var)
00434 yield "else:"
00435 yield INDENT+pack2("'<I%ss'%length", "length, %s"%var)
00436 else:
00437 yield "start = end"
00438 if array_len is not None:
00439 yield "end += %s" % array_len
00440 yield "%s = str[start:end]" % var
00441 else:
00442 yield "end += length"
00443 if base_type in ['uint8', 'char']:
00444 yield "%s = str[start:end]" % (var)
00445 else:
00446 yield "if python3:"
00447 yield INDENT+"%s = str[start:end].decode('utf-8')" % (var)
00448 yield "else:"
00449 yield INDENT+"%s = str[start:end]" % (var)
00450
00451
00452 def array_serializer_generator(msg_context, package, type_, name, serialize, is_numpy):
00453 """
00454 Generator for array types
00455
00456 :raises: :exc:`MsgGenerationException` If array spec is invalid
00457 """
00458 base_type, is_array, array_len = genmsg.msgs.parse_type(type_)
00459 if not is_array:
00460 raise MsgGenerationException("Invalid array spec: %s"%type_)
00461 var_length = array_len is None
00462
00463
00464
00465 if base_type in ['char', 'uint8']:
00466 for y in string_serializer_generator(package, type_, name, serialize):
00467 yield y
00468 return
00469
00470 var = _serial_context+name
00471
00472 if var_length:
00473 for y in len_serializer_generator(var, False, serialize):
00474 yield y
00475 length = None
00476 else:
00477 length = array_len
00478
00479
00480 if is_simple(base_type):
00481 if var_length:
00482 pattern = compute_struct_pattern([base_type])
00483 yield "pattern = '<%%s%s'%%length"%pattern
00484 if serialize:
00485 if is_numpy:
00486 yield pack_numpy(var)
00487 else:
00488 yield pack2('pattern', "*"+var)
00489 else:
00490 yield "start = end"
00491 yield "end += struct.calcsize(pattern)"
00492 if is_numpy:
00493 dtype = NUMPY_DTYPE[base_type]
00494 yield unpack_numpy(var, 'length', dtype, 'str[start:end]')
00495 else:
00496 yield unpack2(var, 'pattern', 'str[start:end]')
00497 else:
00498 pattern = "%s%s"%(length, compute_struct_pattern([base_type]))
00499 if serialize:
00500 if is_numpy:
00501 yield pack_numpy(var)
00502 else:
00503 yield pack(pattern, "*"+var)
00504 else:
00505 yield "start = end"
00506 yield "end += %s"%struct.calcsize('<%s'%pattern)
00507 if is_numpy:
00508 dtype = NUMPY_DTYPE[base_type]
00509 yield unpack_numpy(var, length, dtype, 'str[start:end]')
00510 else:
00511 yield unpack(var, pattern, 'str[start:end]')
00512 if not serialize and base_type == 'bool':
00513
00514 if base_type == 'bool':
00515 yield "%s = map(bool, %s)"%(var, var)
00516
00517 else:
00518
00519
00520
00521
00522 loop_var = 'val%s'%len(_context_stack)
00523
00524
00525 if base_type == 'string':
00526 push_context('')
00527 factory = string_serializer_generator(package, base_type, loop_var, serialize)
00528 else:
00529 push_context('%s.'%loop_var)
00530 factory = serializer_generator(msg_context, get_registered_ex(msg_context, base_type), serialize, is_numpy)
00531
00532 if serialize:
00533 yield 'for %s in %s:'%(loop_var, var)
00534 else:
00535 yield '%s = []'%var
00536 if var_length:
00537 yield 'for i in range(0, length):'
00538 else:
00539 yield 'for i in range(0, %s):'%length
00540 if base_type != 'string':
00541 yield INDENT + '%s = %s'%(loop_var, compute_constructor(msg_context, package, base_type))
00542 for y in factory:
00543 yield INDENT + y
00544 if not serialize:
00545 yield INDENT + '%s.append(%s)'%(var, loop_var)
00546 pop_context()
00547
00548 def complex_serializer_generator(msg_context, package, type_, name, serialize, is_numpy):
00549 """
00550 Generator for serializing complex type
00551
00552 :param serialize: if True, generate serialization
00553 code. Otherwise, deserialization code. ``bool``
00554 :param is_numpy: if True, generate serializer code for numpy
00555 datatypes instead of Python lists, ``bool``
00556 :raises: MsgGenerationException If type is not a valid
00557 """
00558
00559
00560
00561
00562
00563 _, is_array, _ = genmsg.msgs.parse_type(type_)
00564
00565
00566 if is_array:
00567 for y in array_serializer_generator(msg_context, package, type_, name, serialize, is_numpy):
00568 yield y
00569
00570 elif type_ == 'string':
00571 for y in string_serializer_generator(package, type_, name, serialize):
00572 yield y
00573 else:
00574 if not is_special(type_):
00575
00576 pkg, base_type = compute_pkg_type(package, type_)
00577 type_ = "%s/%s"%(pkg, base_type)
00578 if msg_context.is_registered(type_):
00579
00580 ctx_var = next_var()
00581 yield "%s = %s"%(ctx_var, _serial_context+name)
00582 push_context(ctx_var+'.')
00583
00584
00585 for y in serializer_generator(msg_context, get_registered_ex(msg_context, type_), serialize, is_numpy):
00586 yield y
00587 pop_context()
00588 else:
00589
00590 raise MsgGenerationException("Unknown type: %s. Package context is %s"%(type_, package))
00591
00592 def simple_serializer_generator(msg_context, spec, start, end, serialize):
00593 """
00594 Generator (de)serialization code for multiple fields from spec
00595
00596 :param spec: :class:`genmsg.MsgSpec`
00597 :param start: first field to serialize, ``int``
00598 :param end: last field to serialize, ``int``
00599 """
00600
00601 if end - start > 1 and _serial_context.endswith('.'):
00602 yield '_x = '+_serial_context[:-1]
00603 vars_ = '_x.' + (', _x.').join(spec.names[start:end])
00604 else:
00605 vars_ = _serial_context + (', '+_serial_context).join(spec.names[start:end])
00606
00607 pattern = compute_struct_pattern(spec.types[start:end])
00608 if serialize:
00609 yield pack(pattern, vars_)
00610 else:
00611 yield "start = end"
00612 yield "end += %s"%struct.calcsize('<%s'%reduce_pattern(pattern))
00613 yield unpack('(%s,)'%vars_, pattern, 'str[start:end]')
00614
00615
00616
00617
00618 bool_vars = [(f, t) for f, t in zip(spec.names[start:end], spec.types[start:end]) if t == 'bool']
00619 for f, t in bool_vars:
00620
00621 var = _serial_context+f
00622 yield "%s = bool(%s)"%(var, var)
00623
00624 def serializer_generator(msg_context, spec, serialize, is_numpy):
00625 """
00626 Python generator that yields un-indented python code for
00627 (de)serializing MsgSpec. The code this yields is meant to be
00628 included in a class method and cannot be used
00629 standalone. serialize_fn_generator and deserialize_fn_generator
00630 wrap method to provide appropriate class field initializations.
00631
00632 :param serialize: if True, yield serialization
00633 code. Otherwise, yield deserialization code. ``bool``
00634 :param is_numpy: if True, generate serializer code for numpy datatypes instead of Python lists. ``bool``
00635 """
00636
00637
00638
00639 if spec is None:
00640 raise MsgGenerationException("spec is none")
00641 names, types = spec.names, spec.types
00642 if serialize and not len(names):
00643 yield "pass"
00644 return
00645
00646 _max_chunk = 255
00647
00648
00649
00650 curr = 0
00651 for (i, full_type) in enumerate(types):
00652 if not is_simple(full_type):
00653 if i != curr:
00654 for _start in range(curr, i, _max_chunk):
00655 _end = min(_start + _max_chunk, i)
00656 for y in simple_serializer_generator(msg_context, spec, _start, _end, serialize):
00657 yield y
00658 curr = i+1
00659 for y in complex_serializer_generator(msg_context, spec.package, full_type, names[i], serialize, is_numpy):
00660 yield y
00661 if curr < len(types):
00662 for _start in range(curr, len(types), _max_chunk):
00663 _end = min(_start + _max_chunk, len(types))
00664 for y in simple_serializer_generator(msg_context, spec, _start, _end, serialize):
00665 yield y
00666
00667 def serialize_fn_generator(msg_context, spec, is_numpy=False):
00668 """
00669 generator for body of serialize() function
00670 :param is_numpy: if True, generate serializer code for numpy
00671 datatypes instead of Python lists, ``bool``
00672 """
00673
00674 yield "try:"
00675 push_context('self.')
00676
00677
00678 flattened = make_python_safe(flatten(msg_context, spec))
00679 for y in serializer_generator(msg_context, flattened, True, is_numpy):
00680 yield " "+y
00681 pop_context()
00682 yield "except struct.error as se: self._check_types(struct.error(\"%s: '%s' when writing '%s'\" % (type(se), str(se), str(_x))))"
00683 yield "except TypeError as te: self._check_types(ValueError(\"%s: '%s' when writing '%s'\" % (type(te), str(te), str(_x))))"
00684
00685
00686 def deserialize_fn_generator(msg_context, spec, is_numpy=False):
00687 """
00688 generator for body of deserialize() function
00689 :param is_numpy: if True, generate serializer code for numpy
00690 datatypes instead of Python lists, ``bool``
00691 """
00692 yield "try:"
00693 package = spec.package
00694
00695 for type_, name in spec.fields():
00696 if msg_context.is_registered(type_):
00697 yield " if self.%s is None:"%name
00698 yield " self.%s = %s"%(name, compute_constructor(msg_context, package, type_))
00699 yield " end = 0"
00700
00701
00702 push_context('self.')
00703
00704
00705 flattened = make_python_safe(flatten(msg_context, spec))
00706 for y in serializer_generator(msg_context, flattened, False, is_numpy):
00707 yield " "+y
00708 pop_context()
00709
00710
00711
00712 for type_, name in spec.fields():
00713 code = compute_post_deserialize(type_, "self.%s"%name)
00714 if code:
00715 yield " %s"%code
00716
00717 yield " return self"
00718 yield "except struct.error as e:"
00719 yield " raise genpy.DeserializationError(e) #most likely buffer underfill"
00720
00721 def msg_generator(msg_context, spec, search_path):
00722 """
00723 Python code generator for .msg files. Generates a Python from a
00724 :class:`genmsg.MsgSpec`.
00725
00726 :param spec: parsed .msg :class:`genmsg.MsgSpec` instance
00727 :param search_path: dictionary mapping message namespaces to a directory locations
00728 """
00729
00730
00731
00732
00733
00734
00735 try:
00736 genmsg.msg_loader.load_depends(msg_context, spec, search_path)
00737 except InvalidMsgSpec as e:
00738 raise MsgGenerationException("Cannot generate .msg for %s/%s: %s"%(package, name, str(e)))
00739 md5sum = genmsg.compute_md5(msg_context, spec)
00740
00741
00742 spec = make_python_safe(spec)
00743 spec_names = spec.names
00744
00745
00746
00747 clear_patterns()
00748
00749 yield '"""autogenerated by genpy from %s.msg. Do not edit."""'%spec.full_name
00750 yield 'import sys'
00751 yield 'python3 = True if sys.hexversion > 0x03000000 else False'
00752 yield 'import genpy\nimport struct\n'
00753 import_strs = []
00754 for t in spec.types:
00755 import_strs.extend(compute_import(msg_context, spec.package, t))
00756 import_strs = set(import_strs)
00757 for i in import_strs:
00758 if i:
00759 yield i
00760
00761 yield ''
00762
00763 fulltype = spec.full_name
00764 name = spec.short_name
00765
00766
00767 yield 'class %s(genpy.Message):'%spec.short_name
00768 yield ' _md5sum = "%s"'%(md5sum)
00769 yield ' _type = "%s"'%(fulltype)
00770 yield ' _has_header = %s #flag to mark the presence of a Header object'%spec.has_header()
00771
00772 yield ' _full_text = """%s\n"""'%compute_full_text_escaped(msg_context, spec)
00773
00774 if spec.constants:
00775 yield ' # Pseudo-constants'
00776 for c in spec.constants:
00777 if c.type == 'string':
00778 val = c.val
00779 if '"' in val and "'" in val:
00780
00781 escaped = c.val.replace('\\', '\\\\')
00782 escaped = escaped.replace('\"', '\\"')
00783 yield ' %s = "%s"'%(c.name, escaped)
00784 elif '"' in val:
00785 yield " %s = r'%s'"%(c.name, val)
00786 elif "'" in val:
00787 yield ' %s = r"%s"'%(c.name, val)
00788 else:
00789 yield " %s = '%s'"%(c.name, val)
00790 else:
00791 yield ' %s = %s'%(c.name, c.val)
00792 yield ''
00793
00794 if len(spec_names):
00795 yield " __slots__ = ['"+"','".join(spec_names)+"']"
00796 yield " _slot_types = ['"+"','".join(spec.types)+"']"
00797 else:
00798 yield " __slots__ = []"
00799 yield " _slot_types = []"
00800
00801 yield """
00802 def __init__(self, *args, **kwds):
00803 \"\"\"
00804 Constructor. Any message fields that are implicitly/explicitly
00805 set to None will be assigned a default value. The recommend
00806 use is keyword arguments as this is more robust to future message
00807 changes. You cannot mix in-order arguments and keyword arguments.
00808
00809 The available fields are:
00810 %s
00811
00812 :param args: complete set of field values, in .msg order
00813 :param kwds: use keyword arguments corresponding to message field names
00814 to set specific fields.
00815 \"\"\"
00816 if args or kwds:
00817 super(%s, self).__init__(*args, **kwds)"""%(','.join(spec_names), name)
00818
00819 if len(spec_names):
00820 yield " #message fields cannot be None, assign default values for those that are"
00821 for (t, s) in zip(spec.types, spec_names):
00822 yield " if self.%s is None:"%s
00823 yield " self.%s = %s"%(s, default_value(msg_context, t, spec.package))
00824 if len(spec_names) > 0:
00825 yield " else:"
00826 for (t, s) in zip(spec.types, spec_names):
00827 yield " self.%s = %s"%(s, default_value(msg_context, t, spec.package))
00828
00829 yield """
00830 def _get_types(self):
00831 \"\"\"
00832 internal API method
00833 \"\"\"
00834 return self._slot_types
00835
00836 def serialize(self, buff):
00837 \"\"\"
00838 serialize message into buffer
00839 :param buff: buffer, ``StringIO``
00840 \"\"\""""
00841 for y in serialize_fn_generator(msg_context, spec):
00842 yield " "+ y
00843 yield """
00844 def deserialize(self, str):
00845 \"\"\"
00846 unpack serialized message in str into this message instance
00847 :param str: byte array of serialized message, ``str``
00848 \"\"\""""
00849 for y in deserialize_fn_generator(msg_context, spec):
00850 yield " " + y
00851 yield ""
00852
00853 yield """
00854 def serialize_numpy(self, buff, numpy):
00855 \"\"\"
00856 serialize message with numpy array types into buffer
00857 :param buff: buffer, ``StringIO``
00858 :param numpy: numpy python module
00859 \"\"\""""
00860 for y in serialize_fn_generator(msg_context, spec, is_numpy=True):
00861 yield " "+ y
00862 yield """
00863 def deserialize_numpy(self, str, numpy):
00864 \"\"\"
00865 unpack serialized message in str into this message instance using numpy for array types
00866 :param str: byte array of serialized message, ``str``
00867 :param numpy: numpy python module
00868 \"\"\""""
00869 for y in deserialize_fn_generator(msg_context, spec, is_numpy=True):
00870 yield " " + y
00871 yield ""
00872
00873
00874
00875
00876 yield '_struct_I = genpy.struct_I'
00877 patterns = get_patterns()
00878 for p in set(patterns):
00879
00880 if p == 'I':
00881 continue
00882 var_name = '_struct_%s'%(p.replace('<',''))
00883 yield '%s = struct.Struct("<%s")'%(var_name, p)
00884 clear_patterns()
00885
00886 def srv_generator(msg_context, spec, search_path):
00887 for mspec in (spec.request, spec.response):
00888 for l in msg_generator(msg_context, mspec, search_path):
00889 yield l
00890
00891 name = spec.short_name
00892 req, resp = ["%s%s"%(name, suff) for suff in ['Request', 'Response']]
00893
00894 fulltype = spec.full_name
00895
00896 genmsg.msg_loader.load_depends(msg_context, spec, search_path)
00897 md5 = genmsg.compute_md5(msg_context, spec)
00898
00899 yield "class %s(object):"%name
00900 yield " _type = '%s'"%fulltype
00901 yield " _md5sum = '%s'"%md5
00902 yield " _request_class = %s"%req
00903 yield " _response_class = %s"%resp
00904
00905 def _module_name(type_name):
00906 """
00907 :param type_name str: Name of message type sans package,
00908 e.g. 'String'
00909 :returns str: name of python module for auto-generated code
00910 """
00911 return "_"+type_name
00912
00913 def compute_resource_name(filename, ext):
00914 """
00915 Convert resource filename to ROS resource name
00916 :param filename str: path to .msg/.srv file
00917 :returns str: name of ROS resource
00918 """
00919 return os.path.basename(filename)[:-len(ext)]
00920
00921 def compute_outfile_name(outdir, infile_name, ext):
00922 """
00923 :param outdir str: path to directory that files are generated to
00924 :returns str: output file path based on input file name and output directory
00925 """
00926
00927
00928 return os.path.join(outdir, _module_name(compute_resource_name(infile_name, ext))+".py")
00929
00930 class Generator(object):
00931
00932 def __init__(self, what, ext, spec_loader_fn, generator_fn):
00933 self.what = what
00934 self.ext = ext
00935 self.spec_loader_fn = spec_loader_fn
00936 self.generator_fn = generator_fn
00937
00938 def firos_generate(self, msg_context, full_type, topic_data, outdir, search_path):
00939
00940 formatted_msg = ""
00941 for data in topic_data['msg']:
00942 formatted_msg += str(topic_data['msg'][data]) + " " + str(data) + "\n"
00943 formatted_msg = formatted_msg[:-1]
00944 spec = self.spec_loader_fn(msg_context, formatted_msg, full_type)
00945 file_name = full_type.replace("/", "")
00946 outfile = os.path.join(outdir, file_name + ".py")
00947
00948 print("Generating message definition for " + full_type)
00949
00950 with open(outfile, 'w') as f:
00951 for l in self.generator_fn(msg_context, spec, search_path):
00952 f.write(l+'\n')
00953 f.close()
00954 return outfile
00955
00956 def generate(self, msg_context, full_type, f, outdir, search_path):
00957 try:
00958
00959 os.makedirs(outdir)
00960 except OSError as e:
00961 if e.errno != 17:
00962 raise
00963
00964 spec = self.spec_loader_fn(msg_context, f, full_type)
00965 outfile = compute_outfile_name(outdir, os.path.basename(f), self.ext)
00966 with open(outfile, 'w') as f:
00967 for l in self.generator_fn(msg_context, spec, search_path):
00968 f.write(l+'\n')
00969 return outfile
00970
00971 def generate_firos_messages(self, package, data, outdir, OUTPUT, search_path):
00972 """
00973 :returns: return code, ``int``
00974 """
00975 if not genmsg.is_legal_resource_base_name(package):
00976 raise MsgGenerationException("\nERROR: package name '%s' is illegal and cannot be used in message generation.\nPlease see http://ros.org/wiki/Names"%(package))
00977
00978
00979 msg_context = MsgContext.create_default()
00980 retcode = 0
00981 try:
00982
00983 outdir = outdir + OUTPUT
00984 os.makedirs(outdir)
00985 f = open(os.path.join(outdir, "__init__.py"), 'w')
00986 f.close()
00987 except OSError as e:
00988 if e.errno != 17:
00989 raise
00990
00991 for robotName in data:
00992 try:
00993 robot = data[robotName]
00994 for topic_name in robot['topics']:
00995 topic = robot['topics'][topic_name]
00996 full_type = str(robotName) + '/' + str(topic_name)
00997 if type(topic['msg']) is dict:
00998 self.firos_generate(msg_context, full_type, topic, outdir, search_path)
00999 except Exception as e:
01000 if not isinstance(e, MsgGenerationException) and not isinstance(e, genmsg.msgs.InvalidMsgSpec):
01001 traceback.print_exc()
01002 print("\nERROR: Unable to generate %s for package '%s': %s\n"%(self.what, package, e), file=sys.stderr)
01003 retcode = 1
01004 return retcode
01005
01006 def generate_messages(self, package, package_files, outdir, search_path):
01007 """
01008 :returns: return code, ``int``
01009 """
01010 if not genmsg.is_legal_resource_base_name(package):
01011 raise MsgGenerationException("\nERROR: package name '%s' is illegal and cannot be used in message generation.\nPlease see http://ros.org/wiki/Names"%(package))
01012
01013
01014 msg_context = MsgContext.create_default()
01015 retcode = 0
01016 for f in package_files:
01017 try:
01018 f = os.path.abspath(f)
01019 infile_name = os.path.basename(f)
01020 full_type = genmsg.gentools.compute_full_type_name(package, infile_name);
01021 outfile = self.generate(msg_context, full_type, f, outdir, search_path)
01022 except Exception as e:
01023 if not isinstance(e, MsgGenerationException) and not isinstance(e, genmsg.msgs.InvalidMsgSpec):
01024 traceback.print_exc()
01025 print("\nERROR: Unable to generate %s for package '%s': while processing '%s': %s\n"%(self.what, package, f, e), file=sys.stderr)
01026 retcode = 1
01027 return retcode
01028
01029 class SrvGenerator(Generator):
01030
01031 def __init__(self):
01032 super(SrvGenerator, self).__init__('services', genmsg.EXT_SRV,
01033 genmsg.msg_loader.load_srv_from_file,
01034 srv_generator)
01035
01036 class MsgGenerator(Generator):
01037 """
01038 Generates Python message code for all messages in a
01039 package. See genutil.Generator. In order to generator code for a
01040 single .msg file, see msg_generator.
01041 """
01042 def __init__(self, loader=genmsg.msg_loader.load_msg_from_file):
01043 super(MsgGenerator, self).__init__('messages', genmsg.EXT_MSG,
01044 loader,
01045 msg_generator)
01046