3 This is based on the format described at
4 http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/bulletml_ref_e.html.
6 Unless you are adding support for new actions, the only class you
7 should care about in here is BulletML.
10 from __future__
import division
12 from math
import sin
, cos
, radians
, pi
as PI
14 from xml
.etree
.ElementTree
import ElementTree
16 # Python 3 moved this for no really good reason.
18 from sys
import intern
23 from io
import StringIO
26 from cStringIO
import StringIO
28 from StringIO
import StringIO
30 from bulletml
.errors
import Error
31 from bulletml
.expr
import NumberDef
, INumberDef
34 __all__
= ["ParseError", "BulletML"]
38 class ParseError(Error
):
39 """Raised when an error occurs parsing the XML structure."""
43 """Strip namespace poop off the front of a tag."""
45 return element
.tag
.rsplit('}', 1)[1]
49 class ParamList(object):
50 """List of parameter definitions."""
52 def __init__(self
, params
=()):
53 self
.params
= list(params
)
56 def FromXML(cls
, doc
, element
):
57 """Construct using an ElementTree-style element."""
58 return cls([NumberDef(subelem
.text
) for subelem
in element
59 if realtag(subelem
) == "param"])
61 def __call__(self
, params
, rank
):
62 return [param(params
, rank
) for param
in self
.params
]
65 return "%s(%r)" % (type(self
).__name
__, self
.params
)
67 class Direction(object):
68 """Raw direction value."""
70 VALID_TYPES
= ["relative", "absolute", "aim", "sequence"]
72 def __init__(self
, type, value
):
73 if type not in self
.VALID_TYPES
:
74 raise ValueError("invalid type %r" % type)
75 self
.type = intern(type)
78 def __getstate__(self
):
79 return [('type', self
.type), ('value', self
.value
.expr
)]
81 def __setstate__(self
, state
):
83 self
.__init
__(state
["type"], NumberDef(state
["value"]))
86 def FromXML(cls
, doc
, element
, default
="absolute"):
87 """Construct using an ElementTree-style element."""
88 return cls(element
.get("type", default
), NumberDef(element
.text
))
90 def __call__(self
, params
, rank
):
91 return (radians(self
.value(params
, rank
)), self
.type)
94 return "%s(%r, type=%r)" % (
95 type(self
).__name
__, self
.value
, self
.type)
97 class ChangeDirection(object):
98 """Direction change over time."""
100 def __init__(self
, term
, direction
):
102 self
.direction
= direction
104 def __getstate__(self
):
105 return [('frames', self
.term
.expr
),
106 ('type', self
.direction
.type),
107 ('value', self
.direction
.value
.expr
)]
109 def __setstate__(self
, state
):
111 self
.__init
__(INumberDef(state
["frames"]),
112 Direction(state
["type"], NumberDef(state
["value"])))
115 def FromXML(cls
, doc
, element
):
116 """Construct using an ElementTree-style element."""
117 for subelem
in element
.getchildren():
118 tag
= realtag(subelem
)
119 if tag
== "direction":
120 direction
= Direction
.FromXML(doc
, subelem
)
122 term
= INumberDef(subelem
.text
)
124 return cls(term
, direction
)
125 except UnboundLocalError as exc
:
126 raise ParseError(str(exc
))
128 def __call__(self
, owner
, action
, params
, rank
, created
):
129 frames
= self
.term(params
, rank
)
130 direction
, type = self
.direction(params
, rank
)
131 action
.direction_frames
= frames
132 action
.aiming
= False
133 if type == "sequence":
134 action
.direction
= direction
136 if type == "absolute":
137 direction
-= owner
.direction
138 elif type != "relative": # aim or default
140 direction
+= owner
.aim
- owner
.direction
142 # Normalize to [-pi, pi).
143 direction
= (direction
+ PI
) % PI_2
- PI
145 owner
.direction
+= direction
147 action
.direction
= direction
/ frames
150 return "%s(term=%r, direction=%r)" % (
151 type(self
).__name
__, self
.term
, self
.direction
)
154 """Raw speed value."""
156 VALID_TYPES
= ["relative", "absolute", "sequence"]
158 def __init__(self
, type, value
):
159 if type not in self
.VALID_TYPES
:
160 raise ValueError("invalid type %r" % type)
161 self
.type = intern(type)
164 def __getstate__(self
):
165 return [('type', self
.type), ('value', self
.value
.expr
)]
167 def __setstate__(self
, state
):
169 self
.__init
__(state
["type"], NumberDef(state
["value"]))
172 def FromXML(cls
, doc
, element
):
173 """Construct using an ElementTree-style element."""
174 return cls(element
.get("type", "absolute"), NumberDef(element
.text
))
176 def __call__(self
, params
, rank
):
177 return (self
.value(params
, rank
), self
.type)
180 return "%s(%r, type=%r)" % (type(self
).__name
__, self
.value
, self
.type)
182 class ChangeSpeed(object):
183 """Speed change over time."""
185 def __init__(self
, term
, speed
):
189 def __getstate__(self
):
190 return [('frames', self
.term
.expr
),
191 ('type', self
.speed
.type),
192 ('value', self
.speed
.value
.expr
)]
194 def __setstate__(self
, state
):
196 self
.__init
__(INumberDef(state
["frames"]),
197 Speed(state
["type"], NumberDef(state
["value"])))
200 def FromXML(cls
, doc
, element
):
201 """Construct using an ElementTree-style element."""
202 for subelem
in element
.getchildren():
203 tag
= realtag(subelem
)
205 speed
= Speed
.FromXML(doc
, subelem
)
207 term
= INumberDef(subelem
.text
)
209 return cls(term
, speed
)
210 except UnboundLocalError as exc
:
211 raise ParseError(str(exc
))
213 def __call__(self
, owner
, action
, params
, rank
, created
):
214 frames
= self
.term(params
, rank
)
215 speed
, type = self
.speed(params
, rank
)
216 action
.speed_frames
= frames
218 if type == "absolute":
220 elif type == "relative":
222 elif type == "sequence":
224 elif type == "relative":
225 action
.speed
= speed
/ frames
227 action
.speed
= (speed
- owner
.speed
) / frames
230 return "%s(term=%r, speed=%r)" % (
231 type(self
).__name
__, self
.term
, self
.speed
)
234 """Wait for some frames."""
236 def __init__(self
, frames
):
239 def __getstate__(self
):
240 return dict(frames
=self
.frames
.expr
)
242 def __setstate__(self
, state
):
243 self
.__init
__(INumberDef(state
["frames"]))
246 def FromXML(cls
, doc
, element
):
247 """Construct using an ElementTree-style element."""
248 return cls(INumberDef(element
.text
))
250 def __call__(self
, owner
, action
, params
, rank
, created
):
251 action
.wait_frames
= self
.frames(params
, rank
)
255 return "%s(%r)" % (type(self
).__name
__, self
.frames
)
258 """Set a bullet tag."""
260 def __init__(self
, tag
):
263 def __getstate__(self
):
264 return dict(tag
=self
.tag
)
266 def __setstate__(self
, state
):
267 self
.__init
__(state
["tag"])
270 def FromXML(cls
, doc
, element
):
271 """Construct using an ElementTree-style element."""
272 return cls(element
.text
)
274 def __call__(self
, owner
, action
, params
, rank
, created
):
275 owner
.tags
.add(self
.tag
)
278 """Unset a bullet tag."""
280 def __init__(self
, tag
):
283 def __getstate__(self
):
284 return dict(tag
=self
.tag
)
286 def __setstate__(self
, state
):
287 self
.__init
__(state
["tag"])
290 def FromXML(cls
, doc
, element
):
291 """Construct using an ElementTree-style element."""
292 return cls(element
.text
)
294 def __call__(self
, owner
, action
, params
, rank
, created
):
296 owner
.tags
.remove(self
.tag
)
300 class Appearance(object):
301 """Set a bullet appearance."""
303 def __init__(self
, appearance
):
304 self
.appearance
= appearance
306 def __getstate__(self
):
307 return dict(appearance
=self
.appearance
)
309 def __setstate__(self
, state
):
310 self
.__init
__(state
["appearance"])
313 def FromXML(cls
, doc
, element
):
314 """Construct using an ElementTree-style element."""
315 return cls(element
.text
)
317 def __call__(self
, owner
, action
, params
, rank
, created
):
318 owner
.apearance
= self
.appearance
320 class Vanish(object):
321 """Make the owner disappear."""
327 def FromXML(cls
, doc
, element
):
328 """Construct using an ElementTree-style element."""
332 return "%s()" % (type(self
).__name
__)
334 def __call__(self
, owner
, action
, params
, rank
, created
):
338 class Repeat(object):
339 """Repeat an action definition."""
341 def __init__(self
, times
, action
):
345 def __getstate__(self
):
346 return [('times', self
.times
.expr
), ('action', self
.action
)]
348 def __setstate__(self
, state
):
350 self
.__init
__(INumberDef(state
["times"]), state
["action"])
353 def FromXML(cls
, doc
, element
):
354 """Construct using an ElementTree-style element."""
355 for subelem
in element
.getchildren():
356 tag
= realtag(subelem
)
358 times
= INumberDef(subelem
.text
)
359 elif tag
== "action":
360 action
= ActionDef
.FromXML(doc
, subelem
)
361 elif tag
== "actionRef":
362 action
= ActionRef
.FromXML(doc
, subelem
)
364 return cls(times
, action
)
365 except UnboundLocalError as exc
:
366 raise ParseError(str(exc
))
368 def __call__(self
, owner
, action
, params
, rank
, created
):
369 repeat
= self
.times(params
, rank
)
370 actions
, params
= self
.action(params
, rank
)
371 child
= action
.__class
__(
372 owner
, action
, actions
, params
, rank
, repeat
)
373 owner
.replace(action
, child
)
374 child
.step(owner
, created
)
378 return "%s(%r, %r)" % (type(self
).__name
__, self
.times
, self
.action
)
381 """Accelerate over some time."""
386 def __init__(self
, term
, horizontal
=None, vertical
=None):
388 self
.horizontal
= horizontal
389 self
.vertical
= vertical
391 def __getstate__(self
):
392 state
= [('frames', self
.term
.expr
)]
394 state
.append(('horizontal', self
.horizontal
))
396 state
.append(('vertical', self
.vertical
))
399 def __setstate__(self
, state
):
401 self
.__init
__(INumberDef(state
["frames"]), state
.get("horizontal"),
402 state
.get("vertical"))
405 def FromXML(cls
, doc
, element
):
406 """Construct using an ElementTree-style element."""
410 for subelem
in element
.getchildren():
411 tag
= realtag(subelem
)
413 term
= INumberDef(subelem
.text
)
414 elif tag
== "horizontal":
415 horizontal
= Speed
.FromXML(doc
, subelem
)
416 elif tag
== "vertical":
417 vertical
= Speed
.FromXML(doc
, subelem
)
420 return cls(term
, horizontal
, vertical
)
421 except AttributeError:
424 def __call__(self
, owner
, action
, params
, rank
, created
):
425 frames
= self
.term(params
, rank
)
426 horizontal
= self
.horizontal
and self
.horizontal(params
, rank
)
427 vertical
= self
.vertical
and self
.vertical(params
, rank
)
428 action
.accel_frames
= frames
430 mx
, type = horizontal
432 if type == "absolute":
434 elif type == "relative":
436 elif type == "sequence":
438 elif type == "absolute":
439 action
.mx
= (mx
- owner
.mx
) / frames
440 elif type == "relative":
441 action
.mx
= mx
/ frames
445 if type == "absolute":
447 elif type == "relative":
449 elif type == "sequence":
451 elif type == "absolute":
452 action
.my
= (my
- owner
.my
) / frames
453 elif type == "relative":
454 action
.my
= my
/ frames
457 return "%s(%r, horizontal=%r, vertical=%r)" % (
458 type(self
).__name
__, self
.term
, self
.horizontal
, self
.vertical
)
460 class BulletDef(object):
461 """Bullet definition."""
463 def __init__(self
, actions
=(), direction
=None, speed
=None, tags
=(),
465 self
.direction
= direction
467 self
.actions
= list(actions
)
468 self
.tags
= set(tags
)
469 self
.appearance
= appearance
471 def __getstate__(self
):
474 state
.append(("direction", self
.direction
))
476 state
.append(("speed", self
.speed
))
478 state
.append(("actions", self
.actions
))
480 state
.append(("tags", list(self
.tags
)))
482 state
.append(("appearance", self
.appearance
))
485 def __setstate__(self
, state
):
487 self
.__init
__(**state
)
490 def FromXML(cls
, doc
, element
):
491 """Construct using an ElementTree-style element."""
496 for subelem
in element
.getchildren():
497 tag
= realtag(subelem
)
498 if tag
== "direction":
499 direction
= Direction
.FromXML(doc
, subelem
)
501 speed
= Speed
.FromXML(doc
, subelem
)
502 elif tag
== "action":
503 actions
.append(ActionDef
.FromXML(doc
, subelem
))
504 elif tag
== "actionRef":
505 actions
.append(ActionRef
.FromXML(doc
, subelem
))
507 tags
.add(subelem
.text
)
508 dfn
= cls(actions
, direction
, speed
, tags
)
509 doc
._bullets
[element
.get("label")] = dfn
512 def __call__(self
, params
, rank
):
513 actions
= [action(params
, rank
) for action
in self
.actions
]
515 self
.direction
and self
.direction(params
, rank
),
516 self
.speed
and self
.speed(params
, rank
),
522 return "%s(direction=%r, speed=%r, actions=%r)" % (
523 type(self
).__name
__, self
.direction
, self
.speed
, self
.actions
)
525 class BulletRef(object):
526 """Create a bullet by name with parameters."""
528 def __init__(self
, bullet
, params
=None):
530 self
.params
= ParamList() if params
is None else params
532 def __getstate__(self
):
534 if self
.params
.params
:
535 params
= [param
.expr
for param
in self
.params
.params
]
536 state
.append(("params", params
))
537 state
.append(('bullet', self
.bullet
))
540 def __setstate__(self
, state
):
542 bullet
= state
["bullet"]
543 params
= [NumberDef(param
) for param
in state
.get("params", [])]
544 self
.__init
__(bullet
, ParamList(params
))
547 def FromXML(cls
, doc
, element
):
548 """Construct using an ElementTree-style element."""
549 bullet
= cls(element
.get("label"), ParamList
.FromXML(doc
, element
))
550 doc
._bullet
_refs
.append(bullet
)
553 def __call__(self
, params
, rank
):
554 return self
.bullet(self
.params(params
, rank
), rank
)
557 return "%s(params=%r, bullet=%r)" % (
558 type(self
).__name
__, self
.params
, self
.bullet
)
560 class ActionDef(object):
561 """Action definition.
563 To support parsing new actions, add tags to
564 ActionDef.CONSTRUCTORS. It maps tag names to classes with a
565 FromXML classmethod, which take the BulletML instance and
566 ElementTree element as arguments.
569 # This is self-referential, so it's filled in later.
570 CONSTRUCTORS
= dict()
572 def __init__(self
, actions
):
573 self
.actions
= list(actions
)
575 def __getstate__(self
):
576 return dict(actions
=self
.actions
)
578 def __setstate__(self
, state
):
580 self
.__init
__(state
["actions"])
583 def FromXML(cls
, doc
, element
):
584 """Construct using an ElementTree-style element."""
586 for subelem
in element
.getchildren():
587 tag
= realtag(subelem
)
589 ctr
= cls
.CONSTRUCTORS
[tag
]
593 actions
.append(ctr
.FromXML(doc
, subelem
))
595 doc
._actions
[element
.get("label")] = dfn
598 def __call__(self
, params
, rank
):
599 return self
.actions
, params
602 return "%s(%r)" % (type(self
).__name
__, self
.actions
)
604 class ActionRef(object):
605 """Run an action by name with parameters."""
607 def __init__(self
, action
, params
=None):
609 self
.params
= params
or ParamList()
611 def __getstate__(self
):
613 if self
.params
.params
:
614 params
= [param
.expr
for param
in self
.params
.params
]
615 state
.append(("params", params
))
616 state
.append(('action', self
.action
))
619 def __setstate__(self
, state
):
621 action
= state
["action"]
622 params
= [NumberDef(param
) for param
in state
.get("params", [])]
623 self
.__init
__(action
, ParamList(params
))
626 def FromXML(cls
, doc
, element
):
627 """Construct using an ElementTree-style element."""
628 action
= cls(element
.get("label"), ParamList
.FromXML(doc
, element
))
629 doc
._action
_refs
.append(action
)
632 def __call__(self
, params
, rank
):
633 return self
.action(self
.params(params
, rank
), rank
)
636 return "%s(params=%r, action=%r)" % (
637 type(self
).__name
__, self
.params
, self
.action
)
639 class Offset(object):
640 """Provide an offset to a bullet's initial position."""
642 VALID_TYPES
= ["relative", "absolute"]
644 def __init__(self
, type, x
, y
):
645 if type not in self
.VALID_TYPES
:
646 raise ValueError("invalid type %r" % type)
647 self
.type = intern(type)
651 def __getstate__(self
):
652 state
= [('type', self
.type)]
654 state
.append(('x', self
.x
.expr
))
656 state
.append(('y', self
.y
.expr
))
659 def __setstate__(self
, state
):
661 x
= NumberDef(state
["x"]) if "x" in state
else None
662 y
= NumberDef(state
["y"]) if "y" in state
else None
663 self
.__init
__(state
["type"], x
, y
)
666 def FromXML(cls
, doc
, element
):
667 """Construct using an ElementTree-style element."""
668 type = element
.get("type", "relative")
671 for subelem
in element
:
672 tag
= realtag(subelem
)
674 x
= NumberDef(subelem
.text
)
676 y
= NumberDef(subelem
.text
)
677 return cls(type, x
, y
)
679 def __call__(self
, params
, rank
):
680 return (self
.x(params
, rank
) if self
.x
else 0,
681 self
.y(params
, rank
) if self
.y
else 0)
683 class FireDef(object):
684 """Fire definition (creates a bullet)."""
686 def __init__(self
, bullet
, direction
=None, speed
=None, offset
=None,
687 tags
=(), appearance
=None):
689 self
.direction
= direction
692 self
.tags
= set(tags
)
693 self
.appearance
= appearance
695 def __getstate__(self
):
698 state
.append(("direction", self
.direction
))
700 state
.append(("speed", self
.speed
))
702 state
.append(("offset", self
.offset
))
704 state
.append(("tags", list(self
.tags
)))
706 state
.append(("appearance", self
.appearance
))
708 params
= self
.bullet
.params
709 except AttributeError:
710 state
.append(('bullet', self
.bullet
))
713 state
.append(('bullet', self
.bullet
))
715 # Strip out empty BulletRefs.
716 state
.append(('bullet', self
.bullet
.bullet
))
719 def __setstate__(self
, state
):
721 self
.__init
__(**state
)
724 def FromXML(cls
, doc
, element
):
725 """Construct using an ElementTree-style element."""
732 for subelem
in element
.getchildren():
733 tag
= realtag(subelem
)
734 if tag
== "direction":
735 direction
= Direction
.FromXML(doc
, subelem
, "aim")
737 speed
= Speed
.FromXML(doc
, subelem
)
738 elif tag
== "bullet":
739 bullet
= BulletDef
.FromXML(doc
, subelem
)
740 elif tag
== "bulletRef":
741 bullet
= BulletRef
.FromXML(doc
, subelem
)
742 elif tag
== "offset":
743 offset
= Offset
.FromXML(doc
, subelem
)
745 tags
.add(subelem
.text
)
746 elif tag
== "appearance":
747 appearance
= subelem
.text
749 fire
= cls(bullet
, direction
, speed
, offset
, tags
, appearance
)
750 except UnboundLocalError as exc
:
751 raise ParseError(str(exc
))
753 doc
._fires
[element
.get("label")] = fire
756 def __call__(self
, owner
, action
, params
, rank
, created
):
757 direction
, speed
, tags
, appearance
, actions
= self
.bullet(params
, rank
)
759 direction
= self
.direction(params
, rank
)
761 speed
= self
.speed(params
, rank
)
762 tags
= tags
.union(self
.tags
)
764 appearance
= self
.appearance
767 direction
, type = direction
768 if type == "aim" or type is None:
769 direction
+= owner
.aim
770 elif type == "sequence":
771 direction
+= action
.previous_fire_direction
772 elif type == "relative":
773 direction
+= owner
.direction
775 direction
= owner
.aim
776 action
.previous_fire_direction
= direction
780 if type == "sequence":
781 speed
+= action
.previous_fire_speed
782 elif type == "relative":
783 # The reference Noiz implementation uses
784 # prvFireSpeed here, but the standard is
785 # pretty clear -- "In case of the type is
786 # "relative", ... the speed is relative to the
787 # speed of this bullet."
791 action
.previous_fire_speed
= speed
793 x
, y
= owner
.x
, owner
.y
795 off_x
, off_y
= self
.offset(params
, rank
)
796 if self
.offset
.type == "relative":
799 x
+= c
* off_x
+ s
* off_y
800 y
+= s
* off_x
- c
* off_y
805 if appearance
is None:
806 appearance
= owner
.appearance
807 bullet
= owner
.__class
__(
808 x
=x
, y
=y
, direction
=direction
, speed
=speed
,
809 target
=owner
.target
, actions
=actions
, rank
=rank
,
810 appearance
=appearance
, tags
=tags
, Action
=action
.__class
__)
811 created
.append(bullet
)
814 return "%s(direction=%r, speed=%r, bullet=%r)" % (
815 type(self
).__name
__, self
.direction
, self
.speed
, self
.bullet
)
817 class FireRef(object):
818 """Fire a bullet by name with parameters."""
820 def __init__(self
, fire
, params
=None):
822 self
.params
= params
or ParamList()
824 def __getstate__(self
):
826 if self
.params
.params
:
827 params
= [param
.expr
for param
in self
.params
.params
]
828 state
.append(("params", params
))
829 state
.append(('fire', self
.fire
))
832 def __setstate__(self
, state
):
835 params
= [NumberDef(param
) for param
in state
.get("params", [])]
836 self
.__init
__(fire
, ParamList(params
))
839 def FromXML(cls
, doc
, element
):
840 """Construct using an ElementTree-style element."""
841 fired
= cls(element
.get("label"), ParamList
.FromXML(doc
, element
))
842 doc
._fire
_refs
.append(fired
)
845 def __call__(self
, owner
, action
, params
, rank
, created
):
846 params
= self
.params(params
, rank
)
847 return self
.fire(owner
, action
, params
, rank
, created
)
850 return "%s(params=%r, fire=%r)" % (
851 type(self
).__name
__, self
.params
, self
.fire
)
853 class BulletML(object):
854 """BulletML document.
856 A BulletML document is a collection of top-level actions and the
859 You can add tags to the BulletML.CONSTRUCTORS dictionary to extend
860 its parsing. It maps tag names to classes with a FromXML
861 classmethod, which take the BulletML instance and ElementTree
862 element as arguments.
872 def __init__(self
, type="none", actions
=None):
873 self
.type = intern(type)
874 self
.actions
= [] if actions
is None else actions
876 def __getstate__(self
):
877 return [('type', self
.type), ('actions', self
.actions
)]
879 def __setstate__(self
, state
):
881 self
.__init
__(state
["type"], actions
=state
.get("actions"))
884 def FromXML(cls
, source
):
885 """Return a BulletML instance based on XML."""
886 if not hasattr(source
, 'read'):
887 source
= StringIO(source
)
890 root
= tree
.parse(source
)
892 doc
= cls(type=root
.get("type", "none"))
897 doc
._bullet
_refs
= []
898 doc
._action
_refs
= []
901 for element
in root
.getchildren():
902 tag
= realtag(element
)
903 if tag
in doc
.CONSTRUCTORS
:
904 doc
.CONSTRUCTORS
[tag
].FromXML(doc
, element
)
907 for ref
in doc
._bullet
_refs
:
908 ref
.bullet
= doc
._bullets
[ref
.bullet
]
909 for ref
in doc
._fire
_refs
:
910 ref
.fire
= doc
._fires
[ref
.fire
]
911 for ref
in doc
._action
_refs
:
912 ref
.action
= doc
._actions
[ref
.action
]
913 except KeyError as exc
:
914 raise ParseError("unknown reference %s" % exc
)
916 doc
.actions
= [act
for name
, act
in doc
._actions
.items()
917 if name
and name
.startswith("top")]
919 del(doc
._bullet
_refs
)
920 del(doc
._action
_refs
)
929 def FromYAML(cls
, source
):
930 """Create a BulletML instance based on YAML."""
932 # Late import to avoid a circular dependency.
934 import bulletml
.bulletyaml
937 raise ParseError("PyYAML is not available")
940 return yaml
.load(source
)
941 except Exception as exc
:
942 raise ParseError(str(exc
))
945 def FromDocument(cls
, source
):
946 """Create a BulletML instance based on a seekable file or string.
948 This attempts to autodetect if the stream is XML or YAML.
950 if not hasattr(source
, 'read'):
951 source
= StringIO(source
)
952 start
= source
.read(1)
955 return cls
.FromXML(source
)
956 elif start
== "!" or start
== "#":
957 return cls
.FromYAML(source
)
959 raise ParseError("unknown initial character %r" % start
)
962 return "%s(type=%r, actions=%r)" % (
963 type(self
).__name
__, self
.type, self
.actions
)
965 ActionDef
.CONSTRUCTORS
= dict(
969 changeSpeed
=ChangeSpeed
,
970 changeDirection
=ChangeDirection
,
975 appearance
=Appearance
,