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 from __future__ import print_function
34
35 import collections
36 import copy
37 try:
38 from cStringIO import StringIO
39 except ImportError:
40 from io import BytesIO as StringIO
41 import inspect
42 import itertools
43 import os
44 import string
45 import sys
46 import traceback
47
48 import genmsg.msgs
49 import genpy
50 import genpy.dynamic
51
52 import rospkg
53
54 import rosbag
55
56
57
58
61
63 """
64 Check whether a bag file can be played in the current system.
65 @param migrator: message migrator to use
66 @param inbag name of the bag to be checked.
67 @returns A list of tuples for each type in the bag file. The first
68 element of each tuple is the full migration path for the type. The
69 second element of the tuple is the expanded list of invalid rules
70 for that particular path.
71 """
72 checked = set()
73 migrations = []
74
75 bag = rosbag.Bag(inbag, 'r')
76
77 for topic, msg, t in bag.read_messages(raw=True):
78 key = get_message_key(msg[4])
79 if key not in checked:
80 target = migrator.find_target(msg[4])
81
82
83 path = migrator.find_path(msg[4], target)
84 if len(path) > 0:
85 migrations.append((path, [r for r in migrator.expand_rules([sn.rule for sn in path]) if r.valid == False]))
86
87 checked.add(key)
88
89 bag.close()
90
91 return migrations
92
94 """
95 Check whether a bag file can be played in the current system.
96 @param migrator The message migrator to use
97 @param message_list A list of message classes.
98 @returns A list of tuples for each type in the bag file. The first
99 element of each tuple is the full migration path for the type. The
100 second element of the tuple is the expanded list of invalid rules
101 for that particular path.
102 """
103
104 checked = set()
105 migrations = []
106
107 for msg in messages:
108 key = get_message_key(msg)
109 if key not in checked:
110 target = migrator.find_target(msg)
111
112
113 path = migrator.find_path(msg, target)
114 if len(path) > 0:
115 migrations.append((path, [r for r in migrator.expand_rules([sn.rule for sn in path]) if r.valid == False]))
116
117 checked.add(key)
118
119 return migrations
120
121
122
123
124
125
126
127 -def fixbag(migrator, inbag, outbag):
128
129 res = checkbag(migrator, inbag)
130
131
132 if not False in [m[1] == [] for m in res]:
133 bag = rosbag.Bag(inbag, 'r')
134 rebag = rosbag.Bag(outbag, 'w', options=bag.options)
135 for topic, msg, t in bag.read_messages(raw=True):
136 new_msg_type = migrator.find_target(msg[4])
137 mig_msg = migrator.migrate_raw(msg, (new_msg_type._type, None, new_msg_type._md5sum, None, new_msg_type))
138 rebag.write(topic, mig_msg, t, raw=True)
139 rebag.close()
140 bag.close()
141 return True
142 else:
143 return False
144
145
146
147
148
149
150
151 -def fixbag2(migrator, inbag, outbag, force=False):
152
153 res = checkbag(migrator, inbag)
154
155 migrations = [m for m in res if len(m[1]) > 0]
156
157
158 if len(migrations) == 0 or force:
159 bag = rosbag.Bag(inbag, 'r')
160 rebag = rosbag.Bag(outbag, 'w', options=bag.options)
161 for topic, msg, t in bag.read_messages(raw=True):
162 new_msg_type = migrator.find_target(msg[4])
163 if new_msg_type != None:
164 mig_msg = migrator.migrate_raw(msg, (new_msg_type._type, None, new_msg_type._md5sum, None, new_msg_type))
165 rebag.write(topic, mig_msg, t, raw=True)
166 else:
167 rebag.write(topic, msg, t, raw=True)
168 rebag.close()
169 bag.close()
170
171 if force:
172 return []
173 else:
174 return migrations
175
176
177
178
179
180
181
182
183
184
185
187 name_split = name.split('/')
188 try:
189 name_split.remove('std_msgs')
190 except ValueError:
191 pass
192 try:
193 name_split.remove(top_name.split('/')[0])
194 except ValueError:
195 pass
196 new_name = '/'.join(name_split)
197 return new_name
198
199
200
201
202
203
204
205
206
207
209
210 tmp_name = clean_name(name, top_name)
211
212 if len(tmp_name.split('/')) == 2 or (genmsg.msgs.is_builtin(tmp_name)):
213 return tmp_name
214 elif tmp_name == 'Header':
215 return 'std_msgs/Header'
216 else:
217 return top_name.split('/')[0] + '/' + tmp_name
218
219
220
221
222
223
224
225
226
228 try:
229 return (c._type, c._md5sum)
230 except:
231 return None
232
233
234
235
236
237
238
239
240
241
242
248
249
251 old_type = ''
252 old_full_text = ''
253 new_type = ''
254 new_full_text = ''
255 migrated_types = []
256
257 order = -1
258
259 valid = False
260
263
264
265 - def __init__(self, migrator, location):
266
267 self.migrator = migrator
268 self.location = location
269
270 if (self.old_type != self.new_type):
271 self.rename_rule = True
272 else:
273 self.rename_rule = False
274
275
276 try:
277 if self.old_type == "":
278 raise self.EmptyType
279 self.old_types = genpy.dynamic.generate_dynamic(self.old_type, self.old_full_text)
280 self.old_class = self.old_types[self.old_type]
281 self.old_md5sum = self.old_class._md5sum
282 except Exception as e:
283 if not isinstance(e, self.EmptyType):
284 traceback.print_exc(file=sys.stderr)
285 self.old_types = {}
286 self.old_class = None
287 self.old_md5sum = ""
288 try:
289 if self.new_type == "":
290 raise self.EmptyType
291 self.new_types = genpy.dynamic.generate_dynamic(self.new_type, self.new_full_text)
292 self.new_class = self.new_types[self.new_type]
293 self.new_md5sum = self.new_class._md5sum
294 except Exception as e:
295 if not isinstance(e, self.EmptyType):
296 traceback.print_exc(file=sys.stderr)
297 self.new_types = {}
298 self.new_class = None
299 self.new_md5sum = ""
300
301
302
303 self.sub_rules_done = False
304 self.sub_rules_valid = False
305 self.sub_rules = []
306
307
308
309
310
311
312
313
314
315
317 self.sub_rules_valid = True
318 for (t1, t2) in self.migrated_types:
319 try:
320 tmp_old_class = self.get_old_class(t1)
321 except KeyError:
322 print("WARNING: Within rule [%s], specified migrated type [%s] not found in old message types" % (self.location, t1), file=sys.stderr)
323 self.sub_rules_valid = False
324 continue
325 try:
326 tmp_new_class = self.get_new_class(t2)
327 except KeyError:
328 print("WARNING: Within rule [%s], specified migrated type [%s] not found in new message types" % (self.location, t2), file=sys.stderr)
329 self.sub_rules_valid = False
330 continue
331
332
333
334
335 if (get_message_key(tmp_old_class) != get_message_key(self.old_class)) or (get_message_key(tmp_new_class) != get_message_key(self.new_class)):
336 path = self.migrator.find_path(tmp_old_class, tmp_new_class)
337 rules = [sn.rule for sn in path]
338 self.sub_rules.extend(rules)
339
340 if False in [r.valid for r in self.sub_rules]:
341 print("WARNING: Within rule [%s] cannot migrate from subtype [%s] to [%s].." % (self.location, t1, t2), file=sys.stderr)
342 self.sub_rules_valid = False
343 continue
344 self.sub_rules = self.migrator.filter_rules_unique(self.sub_rules)
345 self.sub_rules_done = True
346
347
348
349
350
351
352
354 try:
355 try:
356 return self.new_types[t]
357 except KeyError:
358 return self.new_types['std_msgs/' + t]
359 except KeyError:
360 return self.new_types[self.new_type.split('/')[0] + '/' + t]
361
362
363
364
365
366
367
369 try:
370 try:
371 return self.old_types[t]
372 except KeyError:
373 return self.old_types['std_msgs/' + t]
374 except KeyError:
375 return self.old_types[self.old_type.split('/')[0] + '/' + t]
376
377
378
379
380
381
382
383 - def migrate(self, msg_from, msg_to):
384 tmp_msg_from = clean_name(msg_from._type, self.old_type)
385 tmp_msg_to = clean_name(msg_to._type, self.new_type)
386 if (tmp_msg_from, tmp_msg_to) not in self.migrated_types:
387 raise BagMigrationException("Rule [%s] tried to perform a migration from old [%s] to new [%s] not listed in migrated_types"%(self.location, tmp_msg_from, tmp_msg_to))
388 self.migrator.migrate(msg_from, msg_to)
389
390
391
392
393
394
395
396
397 - def migrate_array(self, msg_from_array, msg_to_array, msg_to_name):
398 msg_to_class = self.get_new_class(msg_to_name)
399
400 while len(msg_to_array) > 0:
401 msg_to_array.pop()
402
403 if (len(msg_from_array) == 0):
404 return
405
406 tmp_msg_from = clean_name(msg_from_array[0]._type, self.old_type)
407 tmp_msg_to = clean_name(msg_to_class._type, self.new_type)
408 if (tmp_msg_from, tmp_msg_to) not in self.migrated_types:
409 raise BagMigrationException("Rule [%s] tried to perform a migration from old [%s] to new [%s] not listed in migrated_types"%(self.location, tmp_msg_from, tmp_msg_to))
410
411 msg_to_array.extend( [msg_to_class() for i in range(len(msg_from_array))] )
412
413 self.migrator.migrate_array(msg_from_array, msg_to_array)
414
415
418
419
420
421
422
423 - def apply(self, old_msg):
438
439
440
441
442
443 - def update(self, old_msg, new_msg):
445
446
447
448
449
450
451
452
453
456 self.chain = []
457 self.order_keys = set()
458 self.rename = None
459
460
461
462
463
464
465
466
467
469 - def __init__(self, old_class, new_class, rule):
470 self.old_class = old_class
471 self.new_class = new_class
472 self.rule = rule
473 self.next = None
474
475
476
477
478
480 - def __init__(self, input_rule_files=[], plugins=True):
481
482
483
484
485
486
487
488 self.rulechains = collections.defaultdict(RuleChain)
489
490
491
492 self.base_nodes = []
493
494
495
496
497 self.extra_nodes = []
498
499
500 self.first_type = {}
501
502
503
504
505 self.rename_map = {}
506
507
508
509 self.found_paths = {}
510 self.found_targets = {}
511
512
513 terminal_nodes = []
514
515
516 rule_dicts = []
517
518 self.false_rule_loaded = False
519
520
521
522 for r in input_rule_files:
523 try:
524 scratch_locals = {'MessageUpdateRule':MessageUpdateRule}
525 with open(r, 'r') as f:
526 exec(f.read(), scratch_locals)
527 rule_dicts.append((scratch_locals, r))
528 except:
529 print("Cannot load rule file [%s] in local package" % r, file=sys.stderr)
530
531
532
533 if plugins:
534 rospack = rospkg.RosPack()
535 for dep,export in [('rosbagmigration','rule_file'),('rosbag','migration_rule_file'),('rosbag_migration_rule','rule_file')]:
536 for pkg in rospack.get_depends_on(dep, implicit=False):
537 m = rospack.get_manifest(pkg)
538 p_rules = m.get_export(dep,export)
539 pkg_dir = rospack.get_path(pkg)
540 for r in p_rules:
541 if dep == 'rosbagmigration':
542 print("""WARNING: The package: [%s] is using a deprecated rosbagmigration export.
543 The export in the manifest should be changed to:
544 <rosbag migration_rule_file="%s"/>
545 """ % (pkg, r), file=sys.stderr)
546 try:
547 scratch_locals = {'MessageUpdateRule':MessageUpdateRule}
548 exec(open(pkg_dir + "/" + r).read(), scratch_locals)
549 rule_dicts.append((scratch_locals, r))
550 except ImportError:
551 print("Cannot load rule file [%s] in package [%s]" % (r, pkg), file=sys.stderr)
552
553
554 for (rule_dict, location_base) in rule_dicts:
555 for (n,c) in rule_dict.items():
556 if inspect.isclass(c):
557 if (not c == MessageUpdateRule) and issubclass(c, MessageUpdateRule):
558 self.add_update_rule(c(self, location_base + ':' + n))
559
560 if self.false_rule_loaded:
561 raise BagMigrationException("Cannot instantiate MessageMigrator with invalid rules")
562
563
564
565
566
567
568
569
570
571
572
573 for (type,rulechain) in self.rulechains.items():
574 first = True
575 sn = None
576 prev_sn = None
577
578
579 rename_set = set([type])
580 tmp = rulechain.rename
581 while tmp:
582 rename_set.add(tmp.new_type)
583 if tmp.new_type in self.rulechains:
584 tmp = self.rulechains[tmp.new_type].rename
585 else:
586 break
587
588 self.rename_map[type] = rename_set
589
590
591 for r in rulechain.chain:
592
593 sn = ScaffoldNode(r.old_class, r.new_class, r)
594 self.base_nodes.append(sn)
595
596 if first:
597 self.first_type[type] = sn
598 first = False
599
600
601 if prev_sn:
602 if get_message_key(prev_sn.new_class) == get_message_key(sn.old_class):
603 prev_sn.next = sn
604 else:
605 implicit_sn = ScaffoldNode(prev_sn.new_class, sn.old_class, None)
606 self.base_nodes.append(implicit_sn)
607 prev_sn.next = implicit_sn
608 implicit_sn.next = sn
609
610 prev_sn = sn
611
612
613 if rulechain.rename:
614
615 sn = ScaffoldNode(rulechain.rename.old_class, rulechain.rename.new_class, rulechain.rename)
616 self.base_nodes.append(sn)
617
618
619
620
621 if first:
622 self.first_type[type] = sn
623 first = False
624 if prev_sn:
625 if get_message_key(prev_sn.new_class) == get_message_key(sn.old_class):
626 prev_sn.next = sn
627 else:
628 implicit_sn = ScaffoldNode(prev_sn.new_class, sn.old_class, None)
629 self.base_nodes.append(implicit_sn)
630 prev_sn.next = implicit_sn
631 implicit_sn.next = sn
632 prev_sn = sn
633 terminal_nodes.append(sn)
634
635 else:
636 if prev_sn:
637 terminal_nodes.append(prev_sn)
638
639
640
641
642
643 for sn in terminal_nodes:
644 key = get_message_key(sn.new_class)
645
646 renamed = (sn.old_class._type != sn.new_class._type)
647
648 sys_class = genpy.message.get_message_class(sn.new_class._type)
649
650
651 if sys_class:
652 new_rule = self.make_update_rule(sn.new_class, sys_class)
653 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
654 if R.valid:
655 sn.next = ScaffoldNode(sn.new_class, sys_class, R)
656 self.base_nodes.append(sn.next)
657
658 if renamed:
659 tmp_sns = self.scaffold_range(sn.new_class._type, sn.new_class._type)
660
661
662 if tmp_sns == []:
663 if sys_class is not None:
664 sn.next = ScaffoldNode(sn.new_class, sys_class, None)
665 self.base_nodes.append(sn.next)
666 continue
667
668
669 for tmp_sn in reversed(tmp_sns):
670 tmp_key = get_message_key(tmp_sn.old_class)
671 if (key == tmp_key):
672 sn.next = tmp_sn
673 break
674
675
676
677
678
679
680
681 if (sn.next is None):
682 for tmp_sn in reversed(tmp_sns):
683 new_rule = self.make_update_rule(sn.new_class, tmp_sn.old_class)
684 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
685 if R.valid:
686 sn.next = ScaffoldNode(sn.new_class, tmp_sn.old_class, R)
687 self.base_nodes.append(sn.next)
688 break
689
690
691
692 if (sn.next is None):
693 if sys_class:
694 new_rule = self.make_update_rule(sn.new_class, sys_class)
695 else:
696 new_rule = self.make_old_half_rule(sn.new_class)
697 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
698 sn.next = ScaffoldNode(sn.new_class, None, R)
699 self.base_nodes.append(sn.next)
700
701
702
703
704
705 for sn in self.base_nodes:
706 if (sn.rule is None):
707 new_rule = self.make_update_rule(sn.old_class, sn.new_class)
708 sn.rule = new_rule(self, 'GENERATED.' + new_rule.__name__)
709
710
711
712 for sn in self.base_nodes:
713 sn.rule.find_sub_paths()
714
715
716
717
718 self.class_dict = {}
719
720 for sn in self.base_nodes + self.extra_nodes:
721 self.class_dict[get_message_key(sn.old_class)] = sn.old_class
722 self.class_dict[get_message_key(sn.new_class)] = sn.new_class
723
724
726 if key in self.class_dict:
727 return self.class_dict[key]
728 else:
729 return None
730
731
733 if r.valid == False:
734 print("ERROR: Update rule [%s] has valid set to False." % (r.location), file=sys.stderr)
735 self.false_rule_loaded = True
736 return
737
738 rulechain = self.rulechains[r.old_type]
739
740 if r.rename_rule:
741 if (rulechain.rename != None):
742 print("WARNING: Update rules [%s] and [%s] both attempting to rename type [%s]. Ignoring [%s]" % (rulechain.rename.location, r.location, r.old_type, r.location), file=sys.stderr)
743 return
744
745
746 cycle = []
747 tmp = r
748 while tmp:
749 cycle.append(tmp)
750 if (tmp.new_type == r.old_type):
751 print("WARNING: Update rules %s introduce a renaming cycle. Ignoring [%s]" % ([x.location for x in cycle], r.location), file=sys.stderr)
752 return
753 if tmp.new_type in self.rulechains:
754 tmp = self.rulechains[tmp.new_type].rename
755 else:
756 break
757
758
759 if rulechain.chain and (r.order <= rulechain.chain[-1].order):
760 print("WARNING: Update rule [%s] which performs rename does not have largest order number. Ignoring" % r.location, file=sys.stderr)
761 return
762
763 rulechain.rename = r
764
765 else:
766 if r.order in rulechain.order_keys:
767 otherind = [x.order for x in rulechain.chain].index(r.order)
768 print("WARNING: Update rules [%s] and [%s] for type [%s] have the same order number. Ignoring [%s]" % (rulechain.chain[otherind].location, r.location, r.old_type, r.location), file=sys.stderr)
769 return
770 else:
771 if rulechain.rename and (r.order >= rulechain.rename.order):
772 print("WARNING: Update rule [%s] has order number larger than rename rule [%s]. Ignoring" % (r.location, rulechain.rename.location), file=sys.stderr)
773 return
774
775 rulechain.order_keys.add(r.order)
776 rulechain.chain.append(r)
777 rulechain.chain.sort(key=lambda x: x.order)
778
779
781 base_valid = not False in [sn.rule.valid for sn in self.base_nodes]
782 extra_valid = not False in [sn.rule.valid for sn in self.extra_nodes]
783 return base_valid and extra_valid
784
785
787 invalid_rules = []
788 invalid_rule_cache = []
789 for sn in self.base_nodes:
790 if not sn.rule.valid:
791 path_key = get_path_key(sn.old_class, sn.new_class)
792 if (path_key not in invalid_rule_cache):
793 invalid_rules.append(sn.rule)
794 invalid_rule_cache.append(path_key)
795 for sn in self.extra_nodes:
796 if not sn.rule.valid:
797 path_key = get_path_key(sn.old_class, sn.new_class)
798 if (path_key not in invalid_rule_cache):
799 invalid_rules.append(sn.rule)
800 invalid_rule_cache.append(path_key)
801 return invalid_rules
802
803
805 rule_cache = []
806 new_rules = []
807 for r in rules:
808 path_key = get_path_key(r.old_class, r.new_class)
809 if (path_key not in rule_cache):
810 new_rules.append(r)
811 return new_rules
812
813
823
825 try:
826 first_sn = self.first_type[old_type]
827
828 sn_range = [first_sn]
829
830 found_new_type = False
831
832 tmp_sn = first_sn
833
834 while (tmp_sn.next is not None and tmp_sn.next.new_class is not None):
835
836 tmp_sn = tmp_sn.next
837 if (tmp_sn != first_sn):
838 sn_range.append(tmp_sn)
839 if (tmp_sn.new_class._type == new_type):
840 found_new_type = True
841 if (found_new_type and tmp_sn.new_class._type != new_type):
842 break
843
844 return sn_range
845
846 except KeyError:
847 return []
848
849
851 key = get_message_key(old_class)
852
853 last_class = old_class
854
855 try:
856 return self.found_targets[key]
857 except KeyError:
858
859 sys_class = genpy.message.get_message_class(old_class._type)
860
861 if sys_class is not None:
862 self.found_targets[key] = sys_class
863 return sys_class
864
865 try:
866 tmp_sn = self.first_type[old_class._type]
867
868 if tmp_sn.new_class is not None:
869 last_class = tmp_sn.new_class
870
871 while tmp_sn.next is not None:
872 tmp_sn = tmp_sn.next
873
874 if tmp_sn.new_class is not None:
875 last_class = tmp_sn.new_class
876 sys_class = genpy.message.get_message_class(tmp_sn.new_class._type)
877 else:
878 sys_class = None
879
880 if sys_class is not None:
881 self.found_targets[key] = sys_class
882 return sys_class
883 except KeyError:
884 pass
885
886 self.found_targets[key] = None
887 return None
888
889
890
892 key = get_path_key(old_class, new_class)
893
894
895 try:
896 return self.found_paths[key]
897 except KeyError:
898 pass
899
900
901
902
903
904 if new_class is None:
905 sn_range = self.scaffold_range(old_class._type, "")
906
907 found_start = False
908
909 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
910
911 if (tmp_sn.old_class._type != old_class._type):
912 continue
913 if get_message_key(tmp_sn.old_class) == get_message_key(old_class):
914 sn_range = sn_range[ind:]
915 found_start = True
916 break
917
918
919 if not found_start:
920 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
921 if (tmp_sn.old_class._type != old_class._type):
922 continue
923 new_rule = self.make_update_rule(old_class, tmp_sn.old_class)
924 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
925 if R.valid:
926 R.find_sub_paths()
927 sn = ScaffoldNode(old_class, tmp_sn.old_class, R)
928 self.extra_nodes.append(sn)
929 sn_range = sn_range[ind:]
930 sn_range.insert(0,sn)
931 found_start = True
932 break
933
934 if sn_range == []:
935 tmp_class = old_class
936 else:
937 tmp_class = sn_range[-1].new_class
938
939 new_rule = self.make_old_half_rule(tmp_class)
940 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
941 sn = ScaffoldNode(tmp_class, None, R)
942 sn_range.append(sn)
943 self.extra_nodes.append(sn)
944 self.found_paths[key] = sn_range
945 return sn_range
946
947
948 if (old_class._type == new_class._type and old_class._full_text.strip() == new_class._full_text.strip()):
949 self.found_paths[key] = []
950 return []
951
952 sn_range = self.scaffold_range(old_class._type, new_class._type)
953
954
955 if sn_range == []:
956 new_rule = self.make_update_rule(old_class, new_class)
957 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
958 R.find_sub_paths()
959 sn = ScaffoldNode(old_class, new_class, R)
960 self.extra_nodes.append(sn)
961 self.found_paths[key] = [sn]
962 return [sn]
963
964
965
966 found_stop = False
967
968
969 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
970
971 if (tmp_sn.new_class._type != new_class._type):
972 break
973 if get_message_key(tmp_sn.new_class) == get_message_key(new_class):
974 sn_range = sn_range[:ind+1]
975 found_stop = True
976 break
977
978
979 if not found_stop:
980 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
981 if (tmp_sn.new_class._type != new_class._type):
982 break
983 new_rule = self.make_update_rule(tmp_sn.new_class, new_class)
984 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
985 if R.valid:
986 R.find_sub_paths()
987 sn = ScaffoldNode(tmp_sn.new_class, new_class, R)
988 self.extra_nodes.append(sn)
989 sn_range = sn_range[:ind+1]
990 sn_range.append(sn)
991 found_stop = True
992 break
993
994
995 if not found_stop:
996 new_rule = self.make_update_rule(sn_range[-1].new_class, new_class)
997 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
998 R.find_sub_paths()
999 sn = ScaffoldNode(sn_range[-1].new_class, new_class, R)
1000 self.extra_nodes.append(sn)
1001 sn_range.append(sn)
1002
1003
1004 found_start = False
1005
1006
1007 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
1008
1009 if (tmp_sn.old_class._type != old_class._type):
1010 continue
1011 if get_message_key(tmp_sn.old_class) == get_message_key(old_class):
1012 sn_range = sn_range[ind:]
1013 found_start = True
1014 break
1015
1016
1017 if not found_start:
1018 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
1019 if (tmp_sn.old_class._type != old_class._type):
1020 continue
1021 new_rule = self.make_update_rule(old_class, tmp_sn.old_class)
1022 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
1023 if R.valid:
1024 R.find_sub_paths()
1025 sn = ScaffoldNode(old_class, tmp_sn.old_class, R)
1026 self.extra_nodes.append(sn)
1027 sn_range = sn_range[ind:]
1028 sn_range.insert(0,sn)
1029 found_start = True
1030 break
1031
1032
1033 if not found_start:
1034 new_rule = self.make_update_rule(old_class, sn_range[0].old_class)
1035 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
1036 R.find_sub_paths()
1037 sn = ScaffoldNode(old_class, sn_range[0].old_class, R)
1038 self.extra_nodes.append(sn)
1039 sn_range.insert(0,sn)
1040
1041 self.found_paths[key] = sn_range
1042 return sn_range
1043
1044
1046 path = self.find_path(msg_from[4], msg_to[4])
1047
1048 if False in [sn.rule.valid for sn in path]:
1049 raise BagMigrationException("Migrate called, but no valid migration path from [%s] to [%s]"%(msg_from[0], msg_to[0]))
1050
1051
1052 if path == [] or msg_from[2] == msg_to[2]:
1053 return (msg_to[0], msg_from[1], msg_to[2], msg_to[3], msg_to[4])
1054
1055 tmp_msg = path[0].old_class()
1056 tmp_msg.deserialize(msg_from[1])
1057
1058 for sn in path:
1059 tmp_msg = sn.rule.apply(tmp_msg)
1060
1061 buff = StringIO()
1062 tmp_msg.serialize(buff)
1063
1064 return (msg_to[0], buff.getvalue(), msg_to[2], msg_to[3], msg_to[4])
1065
1066
1067
1068 - def migrate(self, msg_from, msg_to):
1069 path = self.find_path(msg_from.__class__, msg_to.__class__)
1070
1071 if False in [sn.rule.valid for sn in path]:
1072 raise BagMigrationException("Migrate called, but no valid migration path from [%s] to [%s]"%(msg_from._type, msg_to._type))
1073
1074
1075 if path == [] or msg_from._md5sum == msg_to._md5sum:
1076 buff = StringIO()
1077 msg_from.serialize(buff)
1078 msg_to.deserialize(buff.getvalue())
1079 return
1080
1081 if len(path) > 0:
1082 buff = StringIO()
1083 msg_from.serialize(buff)
1084
1085 tmp_msg = path[0].old_class()
1086
1087 tmp_msg.deserialize(buff.getvalue())
1088
1089 for sn in path:
1090 tmp_msg = sn.rule.apply(tmp_msg)
1091 else:
1092 tmp_msg = msg_from
1093
1094 buff = StringIO()
1095 tmp_msg.serialize(buff)
1096 msg_to.deserialize(buff.getvalue())
1097
1099 if len(msg_from_array) != len(msg_to_array):
1100 raise BagMigrationException("Migrate array called on on arrays of unequal length.")
1101
1102 if len(msg_from_array) == 0:
1103 return
1104
1105 path = self.find_path(msg_from_array[0].__class__, msg_to_array[0].__class__)
1106
1107 if path is None:
1108 raise BagMigrationException("Migrate called, but no migration path from [%s] to [%s]"%(msg_from._type, msg_to._type))
1109
1110
1111 if path == []:
1112 for i in range(len(msg_from_array)):
1113 buff = StringIO()
1114 msg_from_array[i].serialize(buff)
1115 msg_to_array[i].deserialize(buff.getvalue())
1116 return
1117
1118 for i in range(len(msg_from_array)):
1119 buff = StringIO()
1120 tmp_msg = path[0].old_class()
1121 msg_from_array[i].serialize(buff)
1122 tmp_msg.deserialize(buff.getvalue())
1123 for sn in path:
1124 tmp_msg = sn.rule.apply(tmp_msg)
1125
1126 buff = StringIO()
1127 tmp_msg.serialize(buff)
1128 msg_to_array[i].deserialize(buff.getvalue())
1129
1131 name = "update_%s_%s"%(old_class._type.replace("/","_"), old_class._md5sum)
1132
1133
1134
1135 classdef = "class %s(MessageUpdateRule):\n"%name
1136 classdef += "\told_type = \"%s\"\n"%old_class._type
1137 classdef += "\told_full_text = \"\"\"\n%s\n\"\"\"\n\n"%old_class._full_text.strip()
1138 classdef += "\tnew_type = \"%s\"\n"%new_class._type
1139 classdef += "\tnew_full_text = \"\"\"\n%s\n\"\"\"\n"%new_class._full_text.strip()
1140 classdef += "\n"
1141 classdef += "\torder = 0"
1142 classdef += "\n"
1143
1144 validdef = "\tvalid = True\n"
1145
1146 migratedefs = "\tmigrated_types = ["
1147
1148 updatedef = "\tdef update(self, old_msg, new_msg):\n"
1149
1150 old_consts = constants_from_def(old_class._type, old_class._full_text)
1151 new_consts = constants_from_def(new_class._type, new_class._full_text)
1152
1153 if (not new_consts >= old_consts):
1154 validdef = "\tvalid = False\n"
1155 for c in (old_consts - new_consts):
1156 updatedef += "\t\t#Constant '%s' has changed\n"%(c[0],)
1157
1158 old_slots = []
1159 old_slots.extend(old_class.__slots__)
1160
1161 migrations_seen = []
1162
1163
1164 for (s,t) in zip(new_class.__slots__, new_class._slot_types):
1165 warn_msg = None
1166 new_base_type, new_is_array, new_array_len = genmsg.msgs.parse_type(t)
1167 try:
1168 ind = old_class.__slots__.index(s)
1169 old_slots.remove(s)
1170 old_base_type, old_is_array, old_array_len = genmsg.msgs.parse_type(old_class._slot_types[ind])
1171
1172 if new_is_array != old_is_array:
1173 warn_msg = "Could not match array with nonarray"
1174
1175 elif new_array_len != old_array_len:
1176 if old_array_len is None:
1177 warn_msg = "Converted from variable length array to fixed array of length %d"%(new_array_len)
1178 elif new_array_len is None:
1179 warn_msg = "Converted from fixed array of length %d to variable length"%(old_array_len)
1180 else:
1181 warn_msg = "Fixed length array converted from %d to %d"%(old_array_len,new_array_len)
1182
1183 elif genmsg.msgs.is_builtin(new_base_type):
1184 if new_base_type != old_base_type:
1185 warn_msg = "Primitive type changed"
1186 else:
1187 updatedef += "\t\tnew_msg.%s = old_msg.%s\n"%(s,s)
1188
1189 else:
1190 tmp_old_type = clean_name(old_base_type, old_class._type)
1191 tmp_new_type = clean_name(new_base_type, new_class._type)
1192
1193 tmp_qualified_old_type = qualified_name(old_base_type, old_class._type)
1194 tmp_qualified_new_type = qualified_name(new_base_type, new_class._type)
1195
1196
1197 if (tmp_qualified_old_type == tmp_qualified_new_type) or \
1198 (tmp_qualified_old_type in self.rename_map and
1199 tmp_qualified_new_type in self.rename_map[tmp_qualified_old_type]):
1200
1201 if (tmp_old_type, tmp_new_type) not in migrations_seen:
1202 migratedefs += "\n\t\t(\"%s\",\"%s\"),"%(tmp_old_type, tmp_new_type)
1203 migrations_seen.append((tmp_old_type, tmp_new_type))
1204
1205 if not new_is_array:
1206 updatedef += "\t\tself.migrate(old_msg.%s, new_msg.%s)\n"%(s,s)
1207 else:
1208 updatedef += "\t\tself.migrate_array(old_msg.%s, new_msg.%s, \"%s\")\n"%(s,s,new_base_type)
1209 else:
1210 warn_msg = "No migration path between [%s] and [%s]"%(tmp_old_type, tmp_new_type)
1211 except ValueError:
1212 warn_msg = "No matching field name in old message"
1213
1214 if warn_msg is not None:
1215 validdef = "\tvalid = False\n"
1216 updatedef += "\t\t#%s\n"%warn_msg
1217 updatedef += "\t\tnew_msg.%s = %s\n"%(s,migration_default_value(t))
1218
1219 migratedefs += "]\n"
1220
1221 if old_slots:
1222 validdef = "\tvalid = False\n"
1223 for s in old_slots:
1224 updatedef += "\t\t#No field to match field %s from old message\n"%(s)
1225
1226 classdef += migratedefs + '\n' + validdef + '\n' + updatedef
1227
1228 printclassdef = classdef + "\tdef get_class_def(self):\n\t\treturn \'\'\'%s\'\'\'\n"%classdef
1229
1230
1231 exec(printclassdef)
1232 return locals()[name]
1233
1235 name = "update__%s__%s"%(old_class._type.replace("/","_"), old_class._md5sum)
1236
1237
1238
1239 classdef = "class %s(MessageUpdateRule):\n"%name
1240 classdef += "\told_type = \"%s\"\n"%old_class._type
1241 classdef += "\told_full_text = \"\"\"\n%s\n\"\"\"\n\n"%old_class._full_text.strip()
1242 classdef += "\tnew_type = \"\"\n"
1243 classdef += "\tnew_full_text = \"\"\"\n\n\"\"\"\n"
1244 classdef += "\n"
1245 classdef += "\torder = 0"
1246 classdef += "\n"
1247
1248 validdef = "\tvalid = False\n"
1249
1250 migratedefs = "\tmigrated_types = []\n"
1251
1252 updatedef = "\tdef update(self, old_msg, new_msg):\n"
1253 updatedef += "\t\tpass\n"
1254
1255 classdef += migratedefs + '\n' + validdef + '\n' + updatedef
1256
1257 printclassdef = classdef + "\tdef get_class_def(self):\n\t\treturn \'\'\'%s\'\'\'\n"%classdef
1258
1259
1260 exec(printclassdef)
1261 return locals()[name]
1262
1264 name = "update_to_%s_%s"%(new_class._type.replace("/","_"), new_class._md5sum)
1265
1266
1267
1268 classdef = "class %s(MessageUpdateRule):\n"%name
1269 classdef += "\told_type = \"\"\n"
1270 classdef += "\told_full_text = \"\"\"\n\n\"\"\"\n\n"
1271 classdef += "\tnew_type = \"%s\"\n"%new_class._type
1272 classdef += "\tnew_full_text = \"\"\"\n%s\n\"\"\"\n"%new_class._full_text.strip()
1273 classdef += "\n"
1274 classdef += "\torder = 0"
1275 classdef += "\n"
1276
1277 validdef = "\tvalid = False\n"
1278
1279 migratedefs = "\tmigrated_types = []\n"
1280
1281 updatedef = "\tdef update(self, old_msg, new_msg):\n"
1282 updatedef += "\t\tpass\n"
1283
1284 classdef += migratedefs + '\n' + validdef + '\n' + updatedef
1285
1286 printclassdef = classdef + "\tdef get_class_def(self):\n\t\treturn \'\'\'%s\'\'\'\n"%classdef
1287
1288
1289 exec(printclassdef)
1290 return locals()[name]
1291
1293 if field_type in ['bool', 'byte', 'int8', 'int16', 'int32', 'int64',\
1294 'char', 'uint8', 'uint16', 'uint32', 'uint64']:
1295 return '0'
1296 elif field_type in ['float32', 'float64']:
1297 return '0.'
1298 elif field_type == 'string':
1299
1300 return "''"
1301 elif field_type.endswith(']'):
1302 base_type, is_array, array_len = genmsg.msgs.parse_type(field_type)
1303 if base_type in ['byte', 'uint8']:
1304
1305 if array_len is not None:
1306 return "chr(0)*%s"%array_len
1307 else:
1308 return "''"
1309 elif array_len is None:
1310 return '[]'
1311 else:
1312 def_val = migration_default_value(base_type)
1313 return '[' + ','.join(itertools.repeat(def_val, array_len)) + ']'
1314 else:
1315 return "self.get_new_class('%s')()"%field_type
1316
1318 core_pkg, core_base_type = genmsg.package_resource_name(core_type)
1319
1320 splits = msg_def.split('\n' + '=' * 80 + '\n')
1321
1322 core_msg = splits[0]
1323 deps_msgs = splits[1:]
1324
1325
1326 from genmsg import MsgContext
1327 context = MsgContext.create_default()
1328 specs = { core_type: genmsg.msg_loader.load_msg_from_string(context, core_msg, core_pkg) }
1329
1330
1331
1332
1333
1334
1335 return set([(x.name, x.val, x.type) for x in specs[core_type].constants])
1336