Stricter PEP-8 conformance.
[python-bulletml.git] / bulletml / expr.py
index fc71d0d..c7d4098 100644 (file)
@@ -11,6 +11,8 @@ import re
 
 from bulletml.errors import Error
 
+__all__ = ["ExprError", "NumberDef", "INumberDef"]
+
 class ExprError(Error):
     """Raised when an invalid expression is evaluated/compiled."""
     pass
@@ -19,7 +21,6 @@ class NumberDef(object):
     """BulletML numeric expression.
 
     This translates BulletML numeric expressions into Python expressions.
-    The 
 
     Examples:
     35
@@ -37,7 +38,13 @@ class NumberDef(object):
             expr = expr.string
         except AttributeError:
             pass
-        self.string = expr
+        try:
+            if "__" in expr:
+                # nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
+                raise ExprError(expr)
+        except TypeError:
+            pass
+        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")
@@ -50,6 +57,9 @@ class NumberDef(object):
                 if not isinstance(value, (int, float)):
                     raise TypeError(expr)
                 self._value = None
+                self.expr = self.string
+            else:
+                self.expr = self._value
         except Exception:
             raise ExprError(expr)
         self.__expr = compile(self.__expr, __file__, "eval")
@@ -62,7 +72,7 @@ class NumberDef(object):
         return eval(self.__expr, self.GLOBALS, variables)
 
     def __repr__(self):
-        return "%s(%r)" % (type(self).__name__, self.string)
+        return "%s(%r)" % (type(self).__name__, self.expr)
 
 class INumberDef(NumberDef):
     """A NumberDef, but returns rounded integer results."""
@@ -72,6 +82,7 @@ class INumberDef(NumberDef):
             self._value = int(round(self._value))
 
     def __call__(self, params, rank):
+        # Avoid int(round(__call__())) overhead for constants.
         if self._value is not None:
             return self._value
         return int(round(super(INumberDef, self).__call__(params, rank)))