From: Joe Wreschnig Date: Fri, 19 Mar 2010 05:32:51 +0000 (-0700) Subject: BulletML.FromDocument: Type detector. Various setstate bug fixes. X-Git-Url: https://git.yukkurigames.com/?p=python-bulletml.git;a=commitdiff_plain;h=70d25195d561d8b5d3126c6bbac0ecad2a502f06 BulletML.FromDocument: Type detector. Various setstate bug fixes. --- diff --git a/bulletml-runner b/bulletml-runner index 62e9fdf..8bfd338 100755 --- a/bulletml-runner +++ b/bulletml-runner @@ -7,6 +7,12 @@ import time import pygame import bulletml +import bulletml.bulletyaml + +try: + import yaml +except ImportError: + yaml = None try: import psyco diff --git a/bulletml-to-bulletyaml b/bulletml-to-bulletyaml index c5f8c56..ebe0e75 100755 --- a/bulletml-to-bulletyaml +++ b/bulletml-to-bulletyaml @@ -7,7 +7,8 @@ from bulletml import BulletML, bulletyaml def main(argv): for filename in argv: - print yaml.dump(BulletML.FromDocument(open(filename, "rU"))) + print "# Loading", filename + print yaml.dump(yaml.load(yaml.dump(BulletML.FromDocument(open(filename, "rU"))))) if __name__ == "__main__": main(sys.argv[1:]) diff --git a/bulletml/__init__.py b/bulletml/__init__.py index c87a442..addc0fe 100644 --- a/bulletml/__init__.py +++ b/bulletml/__init__.py @@ -6,13 +6,13 @@ Gigawing2, G DARIUS, XEVIOUS, ...) This module parses and executes BulletML scripts in Python. All data structures in it are renderer-agnostic. +In addition to the standard BulletML XML format, this module supports +an equivalent YAML format. See bulletml.bulletyaml for more details. + More information is available at the BulletML homepage, http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html, or the python-bullet homepage, http://code.google.com/p/python-bulletml/. -If you want to use a YAML-based implementation, check out the -bulletml.bulletyaml module in this package. - Basic Usage: from bulletml import Bullet, BulletML diff --git a/bulletml/bulletyaml.py b/bulletml/bulletyaml.py index ea45436..40949bb 100644 --- a/bulletml/bulletyaml.py +++ b/bulletml/bulletyaml.py @@ -33,13 +33,16 @@ def register(Loader=None, Dumper=None): parser.BulletML]: def add(cls, loader, dumper): + """Register a class in a new variable scope.""" tag = "!" + cls.__name__ if loader: def construct(loader, node): + """Construct an object.""" return loader.construct_yaml_object(node, cls) loader.add_constructor(tag, construct) if dumper: def represent(dumper, obj): + """Represent an object.""" return dumper.represent_yaml_object(tag, obj, cls) dumper.add_representer(cls, represent) diff --git a/bulletml/expr.py b/bulletml/expr.py index b72fa22..a00c99e 100644 --- a/bulletml/expr.py +++ b/bulletml/expr.py @@ -37,7 +37,7 @@ class NumberDef(object): expr = expr.string except AttributeError: pass - self.string = str(expr) + self.string = expr = str(expr) repl = lambda match: "params[%d]" % (int(match.group()[1:]) - 1) expr = re.sub(r"\$\d+", repl, expr.lower()) self.__expr = expr.replace("$rand", "random()").replace("$rank", "rank") diff --git a/bulletml/parser.py b/bulletml/parser.py index a89d1c5..10b789a 100644 --- a/bulletml/parser.py +++ b/bulletml/parser.py @@ -24,6 +24,7 @@ except ImportError: from bulletml.errors import Error from bulletml.expr import NumberDef, INumberDef + __all_ = ["ParseError", "BulletML"] class ParseError(Error): @@ -49,15 +50,6 @@ class ParamList(object): return cls([NumberDef(subelem.text) for subelem in element if realtag(subelem) == "param"]) - def __getstate__(self): - if self.params: - return dict(params=[param.expr for param in self.params]) - else: - return {} - - def __setstate__(self, state): - self.__init__(NumberDef(param) for param in state.get("params", [])) - def __call__(self, params, rank): return [param(params, rank) for param in self.params] @@ -321,7 +313,7 @@ class Accel(object): return state def __setstate__(self, state): - self.__init__(INumberDef(state["term"]), state.get("horizontal"), + self.__init__(INumberDef(state["frames"]), state.get("horizontal"), state.get("vertical")) @classmethod @@ -419,13 +411,13 @@ class BulletRef(object): def __getstate__(self): state = dict(bullet=self.bullet) if self.params.params: - state["params"] = self.params.__getstate__() + state["params"] = [param.expr for param in self.params.params] return state def __setstate__(self, state): bullet = state["bullet"] params = [NumberDef(param) for param in state.get("params", [])] - self.__init__(bullet, params) + self.__init__(bullet, ParamList(params)) @classmethod def FromElement(cls, doc, element): @@ -460,7 +452,7 @@ class ActionDef(object): return dict(actions=self.actions) def __setstate__(self, state): - self.__init__(state) + self.__init__(state["actions"]) @classmethod def FromElement(cls, doc, element): @@ -494,13 +486,13 @@ class ActionRef(object): def __getstate__(self): state = dict(action=self.action) if self.params.params: - state["params"] = self.params.__getstate__() + state["params"] = [param.expr for param in self.params.params] return state def __setstate__(self, state): action = state["action"] params = [NumberDef(param) for param in state.get("params", [])] - self.__init__(action, params) + self.__init__(action, ParamList(params)) @classmethod def FromElement(cls, doc, element): @@ -584,6 +576,9 @@ class FireDef(object): state["offset"] = self.offset return state + def __setstate__(self, state): + self.__init__(**state) + @classmethod def FromElement(cls, doc, element): """Construct using an ElementTree-style element.""" @@ -633,13 +628,13 @@ class FireRef(object): def __getstate__(self): state = dict(fire=self.fire) if self.params.params: - state["params"] = self.params.__getstate__() + state["params"] = [param.expr for param in self.params.params] return state def __setstate__(self, state): fire = state["fire"] params = [NumberDef(param) for param in state.get("params", [])] - self.__init__(fire, params) + self.__init__(fire, ParamList(params)) @classmethod def FromElement(cls, doc, element): @@ -684,11 +679,11 @@ class BulletML(object): return dict(type=self.type, actions=self.actions) def __setstate__(self, state): - self.__init__(state["type"], state.get("actions")) + self.__init__(state["type"], actions=state.get("actions")) @classmethod - def FromDocument(cls, source): - """Return a BulletML instance based on a string or file-like.""" + def FromXML(cls, source): + """Return a BulletML instance based on XML.""" if not hasattr(source, 'read'): source = StringIO(source) @@ -726,6 +721,39 @@ class BulletML(object): return self + @classmethod + def FromYAML(cls, source): + """Create a BulletML instance based on YAML.""" + + # Late import to avoid a circular dependency. + try: + import bulletml.bulletyaml + import yaml + except ImportError: + raise ParseError("PyYAML is not available") + else: + try: + return yaml.load(source) + except Exception, exc: + raise ParseError(str(exc)) + + @classmethod + def FromDocument(cls, source): + """Create a BulletML instance based on a seekable file or string. + + This attempts to autodetect if the stream is XML or YAML. + """ + if not hasattr(source, 'read'): + source = StringIO(source) + start = source.read(1) + source.seek(0) + if start == "<": + return cls.FromXML(source) + elif start == "!": + return cls.FromYAML(source) + else: + raise ParseError("unknown initial character %r" % start) + @property def top(self): """Get a list of all top-level actions."""