1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 import os
34 import itertools
35 import math
36 import sys
37 import threading
38 import time
39 from datetime import datetime
40 from python_qt_binding import loadUi
41 from python_qt_binding.QtCore import Qt, QUrl, QTimer, Signal
42 from python_qt_binding.QtGui import QIcon, QTextDocument
43 try:
44 from python_qt_binding.QtGui import QApplication, QDialog
45 except:
46 from python_qt_binding.QtWidgets import QApplication, QDialog
47
48 from roslib import message
49 from genpy.rostime import Time, TVal
50 import rospy
51
52 import gui_resources
53 import node_manager_fkie as nm
54 from node_manager_fkie.common import utf8
55
56
58 """Small helper version to check an object is a string in a way that works
59 for both Python 2 and 3
60 """
61 try:
62 return isinstance(s, basestring)
63 except NameError:
64 return isinstance(s, str)
65
66
68 """
69 Convert atttribute types on the fly, if necessary. This is mainly
70 to convert uint8[] fields back to an array type.
71 """
72 attr = getattr(val, f)
73 if isstring(attr) and 'uint8[' in t:
74 return [ord(x) for x in attr]
75 else:
76 return attr
77
78
80
81 MESSAGE_CHARS_LIMIT = 1000
82 MESSAGE_LINE_LIMIT = 80
83 MESSAGE_HZ_LIMIT = 10
84 MAX_DISPLAY_MSGS = 25
85 STATISTIC_QUEUE_LEN = 1000
86 SHOW_BYTES = True
87 SHOW_JITTER = True
88 SHOW_STD_DEV = False
89 SHOW_WINDOW_SIZE = False
90
91 '''
92 This dialog shows the output of a topic.
93 '''
94
95 finished_signal = Signal(str)
96 '''
97 finished_signal has as parameter the name of the topic and is emitted, if this
98 dialog was closed.
99 '''
100
101 msg_signal = Signal(object, bool)
102 '''
103 msg_signal is a signal, which is emitted, if a new message was received.
104 '''
105
106 text_hz_signal = Signal(str)
107 text_signal = Signal(str)
108 '''
109 text_signal is a signal, which is emitted, if a new text to display was received.
110 '''
111
112 text_error_signal = Signal(str)
113 '''
114 text_error_signal is a signal, which is emitted, if a new error text to display was received.
115 '''
116
117 request_pw = Signal(object)
118
119 - def __init__(self, topic, msg_type, show_only_rate=False, masteruri=None, use_ssh=False, parent=None):
120 '''
121 Creates an input dialog.
122 @param topic: the name of the topic
123 @type topic: C{str}
124 @param msg_type: the type of the topic
125 @type msg_type: C{str}
126 @raise Exception: if no topic class was found for the given type
127 '''
128 QDialog.__init__(self, parent=parent)
129 self._masteruri = masteruri
130 masteruri_str = '' if masteruri is None else '[%s]' % masteruri
131 echo_dialog_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'EchoDialog.ui')
132 loadUi(echo_dialog_file, self)
133 self.setObjectName(' - '.join(['EchoDialog', topic, masteruri_str]))
134 self.setAttribute(Qt.WA_DeleteOnClose, True)
135 self.setWindowFlags(Qt.Window)
136 self.setWindowTitle('%s %s %s' % ('Echo --- ' if not show_only_rate else 'Hz --- ', topic, masteruri_str))
137 self.resize(900, 512)
138
139 self.topic = topic
140 self.show_only_rate = show_only_rate
141 self.lock = threading.RLock()
142 self.last_printed_count = 0
143 self.msg_t0 = -1.
144 self.msg_tn = 0
145 self.times = []
146 self.bytes = []
147
148 self.message_count = 0
149 self._state_message = ''
150 self._state_size_message = ''
151 self._scrapped_msgs = 0
152 self._scrapped_msgs_sl = 0
153
154 self._last_received_ts = 0
155 self.chars_limit = self.MESSAGE_CHARS_LIMIT
156 self.receiving_hz = self.MESSAGE_HZ_LIMIT
157 self.line_limit = self.MESSAGE_LINE_LIMIT
158 self.max_displayed_msgs = self.MAX_DISPLAY_MSGS
159 self.digits_after_in_array = 2
160
161 self.enabled_message_filter = True
162 self.field_filter_fn = None
163 self._latched = False
164 self._msgs = []
165
166 self.filterFrame.setVisible(False)
167 self.topicControlButton.clicked.connect(self.on_topic_control_btn_clicked)
168 self.clearButton.clicked.connect(self.on_clear_btn_clicked)
169 if show_only_rate:
170 self.filterButton.setVisible(False)
171 else:
172 self.filterButton.clicked.connect(self.on_filter_clicked)
173 self.showStringsCheckBox.toggled.connect(self.on_no_str_checkbox_toggled)
174 self.maxLenStringComboBox.activated[str].connect(self.combobox_reduce_ch_activated)
175 self.showArraysCheckBox.toggled.connect(self.on_no_arr_checkbox_toggled)
176 self.maxDigitsComboBox.activated[str].connect(self.combobox_reduce_digits_activated)
177 self.enableMsgFilterCheckBox.toggled.connect(self.on_enable_msg_filter_checkbox_toggled)
178 self.maxLenComboBox.activated[str].connect(self.on_combobox_chars_activated)
179 self.maxHzComboBox.activated[str].connect(self.on_combobox_hz_activated)
180 self.displayCountComboBox.activated[str].connect(self.on_combobox_count_activated)
181 self.combobox_reduce_ch_activated(self.MESSAGE_LINE_LIMIT)
182 self.on_combobox_chars_activated(self.MESSAGE_CHARS_LIMIT)
183 self.on_combobox_hz_activated(self.MESSAGE_HZ_LIMIT)
184 self.on_combobox_count_activated(self.MAX_DISPLAY_MSGS)
185 self.filterButton.setFocus()
186 self.display.setReadOnly(True)
187 self.display.document().setMaximumBlockCount(500)
188 self._blocks_in_msg = None
189 self.display.setOpenLinks(False)
190 self.display.anchorClicked.connect(self._on_display_anchorClicked)
191
192
193 errmsg = ''
194 try:
195 self.__msg_class = message.get_message_class(msg_type)
196 if not self.__msg_class:
197 errmsg = "Cannot load message class for [%s]. Did you build messages?" % msg_type
198 except Exception as e:
199 self.__msg_class = None
200 errmsg = "Cannot load message class for [%s]. Did you build messagest?\nError: %s" % (msg_type, utf8(e))
201
202 self.msg_signal.connect(self._append_message)
203 self.sub = None
204
205
206 self.ssh_output_file = None
207 self.ssh_error_file = None
208 self.ssh_input_file = None
209 self.text_signal.connect(self._append_text)
210 self.text_hz_signal.connect(self._append_text_hz)
211 self._current_msg = ''
212 self._current_errmsg = ''
213 self.text_error_signal.connect(self._append_error_text)
214
215
216 if use_ssh:
217 self.__msg_class = None
218 self._on_display_anchorClicked(QUrl(self._masteruri))
219 elif self.__msg_class is None:
220 errtxt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">\n%s</pre>' % (errmsg)
221 self.display.setText('<a href="%s">open using SSH</a>' % (masteruri))
222 self.display.append(errtxt)
223 else:
224 self.sub = rospy.Subscriber(self.topic, self.__msg_class, self._msg_handle)
225
226 self.print_hz_timer = QTimer()
227 self.print_hz_timer.timeout.connect(self._on_calc_hz)
228 self.print_hz_timer.start(1000)
229 self._start_time = time.time()
230
231
232
233
234
235
236
237
238
240 if self.sub is not None:
241 self.sub.unregister()
242 del self.sub
243 try:
244 self.ssh_output_file.close()
245 self.ssh_error_file.close()
246
247 self.ssh_input_file.write('%s\n' % chr(3))
248 self.ssh_input_file.close()
249 except Exception:
250 pass
251 self.finished_signal.emit(self.topic)
252 if self.parent() is None:
253 QApplication.quit()
254
256 def field_filter(val):
257 try:
258
259
260 for f, t in zip(val.__slots__, val._slot_types):
261 if echo_noarr and '[' in t:
262 continue
263 elif echo_nostr and 'string' in t:
264 continue
265 yield f
266 except Exception:
267 pass
268 return field_filter
269
272
274 self.maxLenStringComboBox.setEnabled(state)
275 self.field_filter_fn = self.create_field_filter(not state, not self.showArraysCheckBox.isChecked())
276
278 self.maxDigitsComboBox.setEnabled(state)
279 self.field_filter_fn = self.create_field_filter(not self.showStringsCheckBox.isChecked(), not state)
280
282 try:
283 self.line_limit = int(ch_txt)
284 except ValueError:
285 try:
286 self.line_limit = float(ch_txt)
287 except ValueError:
288 self.maxLenStringComboBox.setEditText(str(self.line_limit))
289 self.display.clear()
290 with self.lock:
291 for msg, current_time in self._msgs:
292 self._append_message(msg, self._latched, current_time, False)
293
295 try:
296 self.digits_after_in_array = int(ch_txt)
297 except ValueError:
298 self.digits_after_in_array = None
299 self.maxDigitsComboBox.setEditText('')
300 self.display.clear()
301 with self.lock:
302 for msg, current_time in self._msgs:
303 self._append_message(msg, self._latched, current_time, False)
304
306 self.enabled_message_filter = state
307 self.maxLenComboBox.setEnabled(state)
308 self.maxHzComboBox.setEnabled(state)
309 if self.enabled_message_filter:
310 self.on_combobox_chars_activated(self.maxLenComboBox.currentText(), False)
311 self.on_combobox_hz_activated(self.maxHzComboBox.currentText(), False)
312 else:
313 self.chars_limit = 0
314 self.receiving_hz = 0
315 self.display.clear()
316 with self.lock:
317 for msg, current_time in self._msgs:
318 self._append_message(msg, self._latched, current_time, False)
319
321 if not self.enabled_message_filter:
322 return
323 try:
324 self.chars_limit = int(chars_txt)
325 except ValueError:
326 try:
327 self.chars_limit = float(chars_txt)
328 except ValueError:
329 self.maxLenComboBox.setEditText(str(self.chars_limit))
330 if update_display:
331 self.display.clear()
332 with self.lock:
333 for msg, current_time in self._msgs:
334 self._append_message(msg, self._latched, current_time, False)
335
337 if not self.enabled_message_filter:
338 return
339 try:
340 self.receiving_hz = int(hz_txt)
341 except ValueError:
342 try:
343 self.receiving_hz = float(hz_txt)
344 except ValueError:
345 self.maxHzComboBox.setEditText(str(self.receiving_hz))
346 if update_display:
347 self.display.clear()
348 with self.lock:
349 for msg, current_time in self._msgs:
350 self._append_message(msg, self._latched, current_time, False)
351
353 try:
354 self.max_displayed_msgs = int(count_txt)
355 self._blocks_in_msg = None
356 except ValueError:
357 self.displayCountComboBox.setEditText(str(self.max_displayed_msgs))
358
360 self.display.clear()
361 with self.lock:
362 del self._msgs[:]
363 self.message_count = 0
364 self._scrapped_msgs = 0
365 del self.times[:]
366 del self.bytes[:]
367
369 try:
370 if self.sub is None and self.ssh_output_file is None:
371 if self.__msg_class:
372 self.sub = rospy.Subscriber(self.topic, self.__msg_class, self._msg_handle)
373 self._start_time = time.time()
374 else:
375 self._on_display_anchorClicked(QUrl(self._masteruri))
376 self.topicControlButton.setIcon(QIcon(':/icons/deleket_deviantart_stop.png'))
377 else:
378 if self.sub is not None:
379 self.sub.unregister()
380 self.sub = None
381 elif self.ssh_output_file is not None:
382 self.ssh_output_file.close()
383 self.ssh_error_file.close()
384 self.ssh_output_file = None
385 self.topicControlButton.setIcon(QIcon(':/icons/deleket_deviantart_play.png'))
386 except Exception as e:
387 rospy.logwarn('Error while stop/play echo for topic %s: %s' % (self.topic, utf8(e)))
388
391
393 '''
394 Adds a label to the dialog's layout and shows the given text.
395 @param msg: the text to add to the dialog
396 @type msg: message object
397 '''
398 if current_time is None:
399 current_time = time.time()
400 self._latched = latched
401 if store:
402 with self.lock:
403 self._msgs.append((msg, current_time))
404 if len(self._msgs) > 25:
405 self._msgs.pop()
406 msg_len = -1
407 if (self.SHOW_BYTES or self.show_only_rate):
408 buff = None
409 try:
410 from cStringIO import StringIO
411 buff = StringIO()
412 import os
413 msg.serialize(buff)
414 buff.seek(0, os.SEEK_END)
415 msg_len = buff.tell()
416 except ImportError:
417 from io import BytesIO
418 buff = BytesIO()
419 msg.serialize(buff)
420 msg_len = buff.getbuffer().nbytes
421 self._count_messages(current_time, msg_len)
422
423 if self._last_received_ts != 0 and self.receiving_hz != 0:
424 if current_time - self._last_received_ts < 1.0 / self.receiving_hz:
425 if (not latched or (latched and current_time - self._start_time > 3.0)):
426 self._scrapped_msgs += 1
427 self._scrapped_msgs_sl += 1
428 return
429 self._last_received_ts = current_time
430 if not self.show_only_rate:
431
432 msg = self.strify_message(msg, field_filter=self.field_filter_fn)
433 if isinstance(msg, tuple):
434 msg = msg[0]
435 msg = self._trim_width(msg)
436 msg = msg.replace('<', '<').replace('>', '>')
437 msg_cated = False
438 if self.chars_limit != 0 and len(msg) > self.chars_limit:
439 msg = msg[0:self.chars_limit]
440 msg_cated = True
441
442 if self._scrapped_msgs_sl > 0:
443 txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">scrapped %s message because of Hz-settings</pre>' % self._scrapped_msgs_sl
444 self.display.append(txt)
445 self._scrapped_msgs_sl = 0
446 txt = '<pre style="background-color:#FFFCCC; color:#000000;font-family:Fixedsys,Courier; padding:10px;">---------- %s --------------------\n%s</pre>' % (datetime.now().strftime("%d.%m.%Y %H:%M:%S.%f"), msg)
447
448 self._update_max_msg_count(txt)
449 self.display.append(txt)
450 if msg_cated:
451 txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">message has been cut off</pre>'
452 self.display.append(txt)
453 if store:
454 self._print_status()
455
457 '''
458 Counts the received messages. Call this method only on receive message.
459 '''
460 current_time = ts
461 with self.lock:
462
463 if self.msg_t0 < 0 or self.msg_t0 > current_time:
464 self.msg_t0 = current_time
465 self.msg_tn = current_time
466 self.times = []
467 self.bytes = []
468 else:
469 self.times.append(current_time - self.msg_tn)
470 if msg_len > -1:
471 self.bytes.append(msg_len)
472 self.msg_tn = current_time
473
474 if len(self.times) > self.STATISTIC_QUEUE_LEN:
475 self.times.pop(0)
476 if len(self.bytes) > self.STATISTIC_QUEUE_LEN:
477 self.bytes.pop(0)
478 self.message_count += 1
479
481 '''
482 reduce line width to current limit
483 :param msg: the message
484 :type msg: str
485 :return: trimmed message
486 '''
487 result = msg
488 if self.line_limit != 0:
489 a = ''
490 for l in msg.splitlines():
491 a = a + (l if len(l) <= self.line_limit else l[0:self.line_limit - 3] + '...') + '\n'
492 result = a
493 return result
494
496 '''
497 set the count of the displayed messages on receiving the first message
498 :param txt: text of the message, which will be added to the document
499 :type txt: str
500 '''
501 if self._blocks_in_msg is None:
502 td = QTextDocument(txt)
503 self._blocks_in_msg = td.blockCount()
504 self.display.document().setMaximumBlockCount(self._blocks_in_msg * self.max_displayed_msgs)
505
507 if rospy.is_shutdown():
508 self.close()
509 return
510 if not self.show_only_rate and time.time() - self._last_received_ts > 1:
511
512 if self._scrapped_msgs_sl > 0:
513 txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">scrapped %s message because of Hz-settings</pre>' % self._scrapped_msgs_sl
514 self._scrapped_msgs_sl = 0
515 self.display.append(txt)
516 if self.message_count == self.last_printed_count:
517 return
518 with self.lock:
519 message_rate = ''
520 message_bytes = ''
521 message_jitter = ''
522 message_window = ''
523 message_std_dev = ''
524 message_scrapped = ''
525 sum_times = sum(self.times)
526 if sum_times == 0:
527 sum_times = 1
528 if (self.SHOW_BYTES or self.show_only_rate) and self.bytes:
529 sum_bytes = sum(self.bytes)
530 avg = sum_bytes / len(self.bytes)
531 last = self.bytes[-1]
532 if avg != last:
533 message_bytes = "size[ last: %s, avg: %s ]" % (self._normilize_size_print(last), self._normilize_size_print(avg))
534 else:
535 message_bytes = "size: %s" % (self._normilize_size_print(last))
536 byte_rate = float(sum_bytes) / float(sum_times)
537 message_bytes += " bw: %s/s" % (self._normilize_size_print(byte_rate))
538
539 n = len(self.times)
540 if n < 2:
541 return
542 mean = sum_times / n
543 rate = 1. / mean if mean > 0. else 0
544 message_rate = "average rate: %.3f" % rate
545
546 if self.SHOW_JITTER or self.show_only_rate:
547 max_delta = max(self.times)
548 min_delta = min(self.times)
549 message_jitter = "jitter[ min: %.3fs max: %.3fs ]" % (min_delta, max_delta)
550
551 self.last_printed_count = self.message_count
552 if self.SHOW_STD_DEV or self.show_only_rate:
553 std_dev = math.sqrt(sum((x - mean) ** 2 for x in self.times) / n)
554 message_std_dev = "std dev: %.5fs" % (std_dev)
555 if self.SHOW_WINDOW_SIZE or self.show_only_rate:
556 message_window = "window: %s" % (n + 1)
557 if self._scrapped_msgs > 0:
558 message_scrapped += "scrapped msgs: %s" % self._scrapped_msgs
559 self._state_message = ''
560 self._state_size_message = message_bytes
561 for msg in [message_rate, message_jitter, message_std_dev, message_window, message_scrapped]:
562 if msg:
563 if self._state_message:
564 self._state_message += ' '
565 self._state_message += msg
566 self._print_status()
567 if self.show_only_rate:
568 self.display.append("%s %s" % (self._state_message, message_bytes))
569
571 if size > 999999:
572 return "%.2fMiB" % (size / 1048576.0)
573 if size > 999:
574 return "%.2fKiB" % (size / 1024.0)
575 return "%dB" % size
576
578 text = '%s messages %s' % (self.message_count, self._state_message)
579 if self._latched:
580 text = "[latched] %s" % text
581 self.statusLabel.setText(text)
582 self.statusSizeLabel.setText(self._state_size_message)
583
584 - def _append_text(self, text):
585 '''
586 Append echo text received through the SSH.
587 '''
588 with self.lock:
589 self._current_msg += text
590 if self._current_msg.find('---') != -1:
591 messages = self._current_msg.split('---')
592 for m in messages[:-1]:
593 current_time = time.time()
594 self._count_messages(current_time)
595
596 m = self._trim_width(m)
597 txt = '<pre style="background-color:#FFFCCC; color:#000000;font-family:Fixedsys,Courier; padding:10px;">---------- %s --------------------\n%s</pre>' % (datetime.now().strftime("%d.%m.%Y %H:%M:%S.%f"), m)
598
599 self._update_max_msg_count(txt)
600 self.display.append(txt)
601 self._current_msg = messages[-1]
602 self._print_status()
603
604 - def _append_error_text(self, text):
605 '''
606 Append error text received through the SSH.
607 '''
608 with self.lock:
609 self._current_errmsg += text
610 if self._current_errmsg.find('\n') != -1:
611 messages = self._current_errmsg.split('\n')
612 for m in messages[:-1]:
613 txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">%s</pre>' % m
614 self.display.append(txt)
615 self._current_errmsg = messages[-1]
616
617 - def _append_text_hz(self, text):
618 '''
619 Append text received through the SSH for hz view.
620 '''
621 with self.lock:
622 self._current_msg += text
623 if self._current_msg.find('\n') != -1:
624 messages = self._current_msg.split('\n')
625 for m in messages[:-1]:
626 txt = '<div style="font-family:Fixedsys,Courier;">%s</div>' % (m)
627 self.display.append(txt)
628 self._current_msg = messages[-1]
629
631 try:
632 ok = False
633 if self.show_only_rate:
634 self.ssh_input_file, self.ssh_output_file, self.ssh_error_file, ok = nm.ssh().ssh_exec(url.host(), ['rostopic hz %s' % (self.topic)], user, pw, auto_pw_request=True, get_pty=True)
635 self.statusLabel.setText('connected to %s over SSH' % url.host())
636 else:
637 nostr = '--nostr' if not self.showStringsCheckBox.isChecked() else ''
638 noarr = '--noarr' if not self.showArraysCheckBox.isChecked() else ''
639 self.ssh_input_file, self.ssh_output_file, self.ssh_error_file, ok = nm.ssh().ssh_exec(url.host(), ['rostopic echo %s %s %s' % (nostr, noarr, self.topic)], user, pw, auto_pw_request=True, get_pty=True)
640 if ok:
641 self.display.clear()
642 target = self._read_output_hz if self.show_only_rate else self._read_output
643 thread = threading.Thread(target=target, args=((self.ssh_output_file,)))
644 thread.setDaemon(True)
645 thread.start()
646 thread = threading.Thread(target=self._read_error, args=((self.ssh_error_file,)))
647 thread.setDaemon(True)
648 thread.start()
649 elif self.ssh_output_file:
650 self.ssh_output_file.close()
651 self.ssh_error_file.close()
652 except Exception as e:
653 self._append_error_text('%s\n' % e)
654
656 try:
657 while not output_file.closed:
658 text = output_file.read(1)
659 if text:
660 self.text_hz_signal.emit(text)
661 except Exception:
662 pass
663
665 while not output_file.closed:
666 text = output_file.read(1)
667 if text:
668 self.text_signal.emit(text)
669
671 try:
672 while not error_file.closed:
673 text = error_file.read(1)
674 if text:
675 self.text_error_signal.emit(text)
676 except Exception:
677 pass
678
679
680
681
682
683 - def strify_message(self, val, indent='', time_offset=None, current_time=None, field_filter=None, fixed_numeric_width=None):
684 """
685 Convert value to string representation
686 :param val: to convert to string representation. Most likely a Message. ``Value``
687 :param indent: indentation. If indent is set, then the return value will have a leading \n, ``str``
688 :param time_offset: if not None, time fields will be displayed
689 as deltas from time_offset, ``Time``
690
691 :param current_time: currently not used. Only provided for API
692 compatibility. current_time passes in the current time with
693 respect to the message, ``Time``
694 :param field_filter: filter the fields that are strified for Messages, ``fn(Message)->iter(str)``
695 :returns: string (YAML) representation of message, ``str``
696 """
697
698 type_ = type(val)
699 if type_ in (int, long, float) and fixed_numeric_width is not None:
700 if type_ is float:
701 return ('%.' + str(fixed_numeric_width) + 'f') % val
702 else:
703 return ('%d') % val
704 elif type_ in (int, long, float, bool):
705 return utf8(val)
706 elif isstring(val):
707
708 if not val:
709 return "''"
710 return val
711 elif isinstance(val, TVal):
712 if time_offset is not None and isinstance(val, Time):
713 val = val - time_offset
714 if fixed_numeric_width is not None:
715 format_str = '%d'
716 sec_str = '\n%ssecs: ' % indent + (format_str % val.secs)
717 nsec_str = '\n%snsecs: ' % indent + (format_str % val.nsecs)
718 return sec_str + nsec_str
719 else:
720 return '\n%ssecs: %s\n%snsecs: %9d' % (indent, val.secs, indent, val.nsecs)
721
722 elif type_ in (list, tuple):
723 if len(val) == 0:
724 return "[]"
725 val0 = val[0]
726 if type(val0) in (int, float) and self.digits_after_in_array is not None:
727 list_str = '[' + ''.join(self.strify_message(v, indent, time_offset, current_time, field_filter, self.digits_after_in_array) + ', ' for v in val).rstrip(', ') + ']'
728 return list_str
729 elif type(val0) in (int, float, str, bool):
730
731 return utf8(list(val))
732 else:
733 pref = indent + '- '
734 indent = indent + ' '
735 return '\n' + '\n'.join([pref + self.strify_message(v, indent, time_offset, current_time, field_filter, self.digits_after_in_array) for v in val])
736 elif isinstance(val, message.Message):
737
738 if field_filter is not None:
739 fields = list(field_filter(val))
740 else:
741 fields = val.__slots__
742
743 p = '%s%%s: %%s' % (indent)
744 ni = ' ' + indent
745 python_zip = None
746 if sys.hexversion > 0x03000000:
747 python_zip = zip
748 else:
749 python_zip = itertools.izip
750 slots = []
751 for f, t in python_zip(val.__slots__, val._slot_types):
752 if f in fields:
753 cval = _convert_getattr(val, f, t)
754 slot_name = f
755 if isinstance(cval, (list, tuple)):
756 slot_name = "%s[%d]" % (f, len(cval))
757 slots.append(p % (slot_name, self.strify_message(cval, ni, time_offset, current_time, field_filter, fixed_numeric_width)))
758 vals = '\n'.join(slots)
759 if indent:
760 return '\n' + vals
761 else:
762 return vals
763 else:
764 return utf8(val)
765