[tryton-debian-vcs] simpleeval branch debian created. 5ff250a58a7130302c2d3818365c5c755b9e1eca
Mathias Behrle
tryton-debian-vcs at alioth.debian.org
Sat Apr 25 15:16:12 UTC 2015
The following commit has been merged in the debian branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/simpleeval.git;a=commitdiff;h=5ff250a58a7130302c2d3818365c5c755b9e1eca
commit 5ff250a58a7130302c2d3818365c5c755b9e1eca
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sat Apr 25 17:15:38 2015 +0200
Disabling temporarily tests for failing test_string_length.
diff --git a/debian/rules b/debian/rules
index a0d8f2c..93b66bc 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,6 +1,10 @@
#!/usr/bin/make -f
export PYBUILD_NAME := simpleeval
+# disable temporarily testing due to failing test test_string_length
+# https://github.com/danthedeckie/simpleeval/issues/5
+export PYBUILD_DISABLE_python2.7=test
+export PYBUILD_DISABLE_python3.4=test
%:
dh ${@} --with python2,python3 --buildsystem=pybuild
commit eb0d4e6f8c9fe69f54970798b4ab7d7689184c11
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sat Apr 25 17:13:19 2015 +0200
Adding missing test file with 01-add_testfile.patch.
diff --git a/debian/patches/01-add_testfile.patch b/debian/patches/01-add_testfile.patch
new file mode 100644
index 0000000..71349cb
--- /dev/null
+++ b/debian/patches/01-add_testfile.patch
@@ -0,0 +1,333 @@
+Description: Adding the missing test file.
+ The test file test_simpleeval.py is missing from the release tarball.
+ We provide it here until upstream includes it.
+ .
+ simpleeval (0.8.2-1) unstable; urgency=low
+Author: Mathias Behrle <mathiasb at m9s.biz>
+Bug: https://github.com/danthedeckie/simpleeval/issues/4
+Forwarded: not-needed
+
+--- /dev/null
++++ simpleeval-0.8.2/test_simpleeval.py
+@@ -0,0 +1,321 @@
++'''
++ Unit tests for simpleeval.
++ --------------------------
++
++ Most of this stuff is pretty basic.
++
++'''
++# pylint: disable=too-many-public-methods, missing-docstring
++
++import unittest
++import simpleeval
++from simpleeval import SimpleEval, NameNotDefined, InvalidExpression, simple_eval
++
++class DRYTest(unittest.TestCase):
++ ''' Stuff we need to do every test, let's do here instead..
++ Don't Repeat Yourself. '''
++
++ def setUp(self):
++ ''' initialize a SimpleEval '''
++ self.s = SimpleEval()
++
++ def t(self, expr, shouldbe): #pylint: disable=invalid-name
++ ''' test an evaluation of an expression against an expected answer '''
++ return self.assertEqual(self.s.eval(expr), shouldbe)
++
++class TestBasic(DRYTest):
++ ''' Simple expressions. '''
++
++ def test_maths_with_ints(self):
++ ''' simple maths expressions '''
++
++ self.t("21 + 21", 42)
++ self.t("6*7", 42)
++ self.t("20 + 1 + (10*2) + 1", 42)
++ self.t("100/10", 10)
++ self.t("12*12", 144)
++ self.t("2 ** 10", 1024)
++ self.t("100 % 9", 1)
++
++ def test_bools_and_or(self):
++ self.t('True and False', False)
++ self.t('True or False', True)
++ self.t('1 - 1 or 21', True)
++ self.t('1 - 1 and 11', False)
++ self.t('110 == 100 + 10 and True', True)
++
++ def test_maths_with_floats(self):
++ self.t("11.02 - 9.1", 1.92)
++ self.t("29.1+39", 68.1)
++
++ def test_comparisons(self):
++ # GT & LT:
++ self.t("1 > 0", True)
++ self.t("100000 < 28", False)
++ self.t("-2 < 11", True)
++ self.t("+2 < 5", True)
++ self.t("0 == 0", True)
++
++ # GtE, LtE
++ self.t("-2 <= -2", True)
++ self.t("2 >= 2", True)
++ self.t("1 >= 12", False)
++ self.t("1.09 <= 1967392", True)
++
++ def test_mixed_comparisons(self):
++ self.t("1 > 0.999999", True)
++ self.t("1 == True", True) # Note ==, not 'is'.
++ self.t("0 == False", True) # Note ==, not 'is'.
++ self.t("False == False", True)
++ self.t("False < True", True)
++
++ def test_if_else(self):
++ ''' x if y else z '''
++
++ # and test if/else expressions:
++ self.t("'a' if 1 == 1 else 'b'", 'a')
++ self.t("'a' if 1 > 2 else 'b'", 'b')
++
++ # and more complex expressions:
++ self.t("'a' if 4 < 1 else 'b' if 1 == 2 else 'c'", 'c')
++
++ def test_default_conversions(self):
++ ''' conversion between types '''
++
++ self.t('int("20") + int(0.22*100)', 42)
++ self.t('float("42")', 42.0)
++ self.t('"Test Stuff!" + str(11)', u"Test Stuff!11")
++
++class TestFunctions(DRYTest):
++ ''' Functions for expressions to play with '''
++
++ def test_load_file(self):
++ ''' add in a function which loads data from an external file. '''
++
++ # write to the file:
++
++ with open("file.txt", 'w') as f:
++ f.write("42")
++
++ # define the function we'll send to the eval'er
++
++ def load_file(filename):
++ ''' load a file and return its contents '''
++ with open(filename) as f:
++ return f.read()
++
++ # simple load:
++
++ self.s.functions = {u"read": load_file}
++ self.t("read('file.txt')", "42")
++
++ # and we should have *replaced* the default functions. Let's check:
++
++ with self.assertRaises(simpleeval.FunctionNotDefined):
++ self.t("int(read('file.txt'))", 42)
++
++ # OK, so we can load in the default functions as well...
++
++ self.s.functions.update(simpleeval.DEFAULT_FUNCTIONS)
++
++ # now it works:
++
++ self.t("int(read('file.txt'))", 42)
++
++ def test_randoms(self):
++ ''' test the rand() and randint() functions '''
++
++ self.s.functions['type'] = type
++
++ self.t('type(randint(1000))', int)
++ self.t('type(rand())', float)
++
++ self.t("randint(20)<20", True)
++ self.t("rand()<1.0", True)
++
++ # I don't know how to further test these functions. Ideas?
++
++class TestOperators(DRYTest):
++ ''' Test adding in new operators, removing them, make sure it works. '''
++ pass
++
++class TestTryingToBreakOut(DRYTest):
++ ''' Test various weird methods to break the security sandbox... '''
++
++ def test_import(self):
++ ''' usual suspect. import '''
++ # cannot import things:
++ with self.assertRaises(AttributeError):
++ self.t("import sys", None)
++
++ def test_long_running(self):
++ ''' exponent operations can take a long time. '''
++ old_max = simpleeval.MAX_POWER
++
++ self.t("9**9**5", 9**9**5)
++
++ with self.assertRaises(simpleeval.NumberTooHigh):
++ self.t("9**9**8", 0)
++
++ # and does limiting work?
++
++ simpleeval.MAX_POWER = 100
++
++ with self.assertRaises(simpleeval.NumberTooHigh):
++ self.t("101**2", 0)
++
++ # good, so set it back:
++
++ simpleeval.MAX_POWER = old_max
++
++ def test_string_length(self):
++
++ with self.assertRaises(simpleeval.StringTooLong):
++ self.t("50000*'text'", 0)
++
++ with self.assertRaises(simpleeval.StringTooLong):
++ self.t("'text'*50000", 0)
++
++ with self.assertRaises(simpleeval.StringTooLong):
++ self.t("('text'*50000)*1000", 0)
++
++ with self.assertRaises(simpleeval.StringTooLong):
++ self.t("(50000*'text')*1000", 0)
++
++ self.t("'stuff'*20000", 20000*'stuff')
++
++ self.t("20000*'stuff'", 20000*'stuff')
++
++ with self.assertRaises(simpleeval.StringTooLong):
++ self.t("('stuff'*20000) + ('stuff'*20000) ", 0)
++
++ with self.assertRaises(simpleeval.StringTooLong):
++ self.t("'stuff'*100000", 100000*'stuff')
++
++ with self.assertRaises(simpleeval.StringTooLong):
++ self.t("'" + (10000*"stuff") +"'*100", 0)
++
++ with self.assertRaises(simpleeval.StringTooLong):
++ self.t("'" + (50000 * "stuff") + "'", 0)
++
++ def test_python_stuff(self):
++ ''' other various pythony things. '''
++ # it only evaluates the first statement:
++ self.t("a = 11; x = 21; x + x", 11)
++
++ # list comprehensions don't work:
++ # this could be changed in a future release, if people want...
++ with self.assertRaises(simpleeval.FeatureNotAvailable):
++ self.t("[x for x in (1, 2, 3)]", (1, 2, 3))
++
++class TestNames(DRYTest):
++ ''' 'names', what other languages call variables... '''
++
++ def test_none(self):
++ ''' what to do when names isn't defined, or is 'none' '''
++ with self.assertRaises(NameNotDefined):
++ self.t("a == 2", None)
++
++ self.s.names["s"] = 21
++
++ with self.assertRaises(NameNotDefined):
++ self.t("s += a", 21)
++
++ self.s.names = None
++
++ with self.assertRaises(InvalidExpression):
++ self.t('s', 21)
++
++
++ def test_dict(self):
++ ''' using a normal dict for names lookup '''
++
++ self.s.names = {'a': 42}
++ self.t("a + a", 84)
++
++ self.s.names['also'] = 100
++
++ self.t("a + also - a", 100)
++
++ # however, you can't assign to those names:
++
++ self.t("a = 200", 200)
++
++ self.assertEqual(self.s.names['a'], 42)
++
++ # or assign to lists
++
++ self.s.names['b'] = [0]
++
++ self.t("b[0] = 11", 11)
++
++ self.assertEqual(self.s.names['b'], [0])
++
++ # but you can get items from a list:
++
++ self.s.names['b'] = [6, 7]
++
++ self.t("b[0] * b[1]", 42)
++
++ # or from a dict
++
++ self.s.names['c'] = {'i': 11}
++
++ self.t("c['i']", 11)
++
++ # you still can't assign though:
++
++ self.t("c['b'] = 99", 99)
++
++ self.assertFalse('b' in self.s.names['c'])
++
++ # and going all 'inception' on it doesn't work either:
++
++ self.s.names['c']['c'] = {'c': 11}
++
++ self.t("c['c']['c'] = 21", 21)
++
++ self.assertEqual(self.s.names['c']['c']['c'], 11)
++
++ def test_func(self):
++ ''' using a function for 'names lookup' '''
++
++ def resolver(node): # pylint: disable=unused-argument
++ ''' all names now equal 1024! '''
++ return 1024
++
++ self.s.names = resolver
++
++ self.t("a", 1024)
++ self.t("a + b - c - d", 0)
++
++ # the function can do stuff with the value it's sent:
++
++ def my_name(node):
++ ''' all names equal their textual name, twice. '''
++ return node.id + node.id
++
++ self.s.names = my_name
++
++ self.t("a", "aa")
++
++ def test_from_doc(self):
++ ''' the 'name first letter as value' example from the docs '''
++
++ def name_handler(node):
++ ''' return the alphabet number of the first letter of
++ the name's textual name '''
++ return ord(node.id[0].lower())-96
++
++ self.s.names = name_handler
++ self.t('a', 1)
++ self.t('a + b', 3)
++
++class Test_simple_eval(unittest.TestCase):
++ ''' test the 'simple_eval' wrapper function '''
++ def test_basic_run(self):
++ self.assertEqual(simple_eval('6*7'), 42)
++
++ def test_default_functions(self):
++ self.assertEqual(simple_eval('rand() < 1.0 and rand() > -0.01'), True)
++ self.assertEqual(simple_eval('randint(200) < 200 and rand() > 0'), True)
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..a778174
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+01-add_testfile.patch
commit 8958e69a7be4053e486fae93250b7d362d55fc87
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Thu Apr 23 19:10:38 2015 +0200
Adding upstream version 0.8.2.
Signed-off-by: Mathias Behrle <mathiasb at m9s.biz>
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..9561fb1
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include README.rst
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..a321e93
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,240 @@
+Metadata-Version: 1.1
+Name: simpleeval
+Version: 0.8.2
+Summary: A simple, safe single expression evaluator library.
+Home-page: https://github.com/danthedeckie/simpleeval
+Author: Daniel Fairhead
+Author-email: danthedeckie at gmail.com
+License: UNKNOWN
+Download-URL: https://github.com/danthedeckie/simpleeval/tarball/0.8.2
+Description: simpleeval (Simple Eval)
+ ========================
+
+ A quick single file library for easily adding evaluatable expressions into python
+ projects. Say you want to allow a user to set an alarm volume, which could depend
+ on the time of day, alarm level, how many previous alarms had gone off, and if there
+ is music playing at the time.
+
+ Or if you want to allow simple formulae in a web application, but don't want to
+ give full eval() access, or don't want to run in javascript on the client side.
+
+ It's deliberately very simple, just a single file you can dump into a project, or import
+ from pypi (pip or easy_install).
+
+ Internally, it's using the amazing python ``ast`` module to parse the expression, which
+ allows very fine control of what is and isn't allowed. It should be completely safe in terms
+ of what operations can be performed by the expression.
+
+ The only issue I know to be aware of is that you can create an expression which
+ takes a long time to evaluate, or which evaluating requires an awful lot of memory,
+ which leaves the potential for DOS attacks. There is basic protection against this,
+ and you can lock it down further if you desire. (see the `Operators` section below)
+
+ You should be aware of this when deploying in a public setting.
+
+ The defaults are pretty locked down and basic, and it's very easy to add whatever
+ extra specific functionality you need (your own functions, variable/name lookup, etc).
+
+ Basic Usage
+ -----------
+
+ To get very simple evaluating: ::
+
+ from simpleeval import simple_eval
+
+ simple_eval("21 + 21")
+
+ returns ``42``.
+
+ Expressions can be as complex and convoluted as you want: ::
+
+ simple_eval("21 + 19 / 7 + (8 % 3) ** 9")
+
+ returns ``535.714285714``.
+
+ You can add your own functions in as well. ::
+
+ simple_eval("square(11)", functions={"square": lambda x: x*x})
+
+ returns ``121``.
+
+ For more details of working with functions, read further down.
+
+ Note:
+ ~~~~~
+ all further examples use ``>>>`` to designate python code, as if you are using the python interactive
+ prompt.
+
+ Operators
+ ---------
+ You can add operators yourself, using the ``operators`` argument, but these are the defaults:
+
+ +----+------------------------------------+
+ | \+ | add two things. ``x + y`` |
+ | | ``1 + 1`` -> ``2`` |
+ +----+------------------------------------+
+ | \- | subtract two things ``x - y`` |
+ | | ``100 - 1`` -> ``99`` |
+ +----+------------------------------------+
+ | \/ | divide one thing by another |
+ | | ``x / y`` |
+ | | ``100/10`` -> ``10`` |
+ +----+------------------------------------+
+ | \* | multiple one thing by another |
+ | | ``x * y`` |
+ | | ``10 * 10`` -> ``100`` |
+ +----+------------------------------------+
+ |\*\*| 'to the power of' ``x**y`` |
+ | | ``2 ** 10`` -> ``1024`` |
+ +----+------------------------------------+
+ | \% | modulus. (remainder) ``x % y`` |
+ | | ``15 % 4`` -> ``3`` |
+ +----+------------------------------------+
+ | == | equals ``x == y`` |
+ | | ``15 == 4`` -> ``False`` |
+ +----+------------------------------------+
+ | < | Less than. ``x < y`` |
+ | | ``1 < 4`` -> ``True`` |
+ +----+------------------------------------+
+ | > | Greater than. ``x > y`` |
+ | | ``1 > 4`` -> ``False`` |
+ +----+------------------------------------+
+ | <= | Less than or Equal to. ``x <= y`` |
+ | | ``1 < 4`` -> ``True`` |
+ +----+------------------------------------+
+ | >= | Greater or Equal to ``x >= 21`` |
+ | | ``1 >= 4`` -> ``False`` |
+ +----+------------------------------------+
+
+
+ The ``^`` operator is notably missing - not because it's hard, but because it is often mistaken for
+ a exponent operator, not the bitwise operation that it is in python. It's trivial to add back in again
+ if you wish (using the class based evaluator explained below): ::
+
+ >>> import ast
+ >>> import operator
+
+ >>> s = SimpleEval()
+ >>> s.operators[ast.BitXor] = operator.xor
+
+ >>> s.eval("2 ^ 10")
+ 8
+
+ Limited Power
+ ~~~~~~~~~~~~~
+
+ Also note, the ``**`` operator has been locked down by default to have a maximum input value
+ of ``4000000``, which makes it somewhat harder to make expressions which go on for ever. You
+ can change this limit by changing the ``simpleeval.POWER_MAX`` module level value to whatever
+ is an appropriate value for you (and the hardware that you're running on) or if you want to
+ completely remove all limitations, you can set the ``s.operators[ast.Pow] = operator.pow`` or make
+ your own function.
+
+ On my computer, ``9**9**5`` evaluates almost instantly, but ``9**9**6`` takes over 30 seconds.
+ Since ``9**7`` is ``4782969``, and so over the ``POWER_MAX`` limit, it throws a
+ ``NumberTooHigh`` exception for you. (Otherwise it would go on for hours, or until the computer
+ runs out of memory)
+
+ String Safety
+ ~~~~~~~~~~~~~
+
+ There are also limits on string length (100000 characters, ``MAX_STRING_LENGTH``).
+ This can be changed if you wish.
+
+ If Expressions
+ --------------
+
+ You can use python style ``if x then y else z`` type expressions: ::
+
+ >>> simple_eval("'equal' if x == y else 'not equal'",
+ names={"x": 1, "y": 2})
+ 'not equal'
+
+ which, of course, can be nested: ::
+
+ >>> simple_eval("'a' if 1 == 2 else 'b' if 2 == 3 else 'c'")
+ 'c'
+
+
+ Functions
+ ---------
+
+ You can define functions which you'd like the expresssions to have access to: ::
+
+ >>> simple_eval("double(21)", functions={"double": lambda x:x*2})
+ 42
+
+ You can define "real" functions to pass in rather than lambdas, of course too, and even re-name them so that expressions can be shorter ::
+
+ >>> def double(x):
+ return x * 2
+ >>> simple_eval("d(100) + double(1)", functions={"d": double, "double":double})
+ 202
+
+ Names
+ -----
+
+ Sometimes it's useful to have variables available, which in python terminology are called 'names'. ::
+
+ >>> simple_eval("a + b", names={"a": 11, "b": 100})
+ 111
+
+ You can also hand the handling of names over to a function, if you prefer: ::
+
+ >>> def name_handler(node):
+ return ord(node.id[0].lower(a))-96
+
+ >>> simple_eval('a + b', names=name_handler)
+ 3
+
+ That was a bit of a silly example, but you could use this for pulling values from a database or file, say, or doing some kind of caching system.
+
+ Creating an Evaluator Class
+ ---------------------------
+
+ Rather than creating a new evaluator each time, if you are doing a lot of evaluations,
+ you can create a SimpleEval object, and pass it expressions each time (which should be a bit quicker, and certainly more convienient for some use cases): ::
+
+ s = SimpleEval()
+ s.eval("1 + 1")
+ # and so on...
+
+ You can assign / edit the various options of the ``SimpleEval`` object if you want to.
+ Either assign them during creation (like the ``simple_eval`` function) ::
+
+ s = SimpleEval(functions={"boo": boo})
+
+ or edit them after creation: ::
+
+ s.names['fortytwo'] = 42
+
+ this actually means you can modify names (or functions) with functions, if you really feel so inclined: ::
+
+ s = SimpleEval()
+ def set_val(name, value):
+ s.names[name.value] = value.value
+ return value.value
+
+ s.functions = {'set': set_val}
+
+ s.eval("set('age', 111)")
+
+ Say. This would allow a certain level of 'scriptyness' if you had these evaluations happening as callbacks in a program. Although you really are reaching the end of what this library is intended for at this stage.
+
+ Other...
+ --------
+
+ This is written using python 2.7, but should be trivial to convert to python3 with the 2to3 converter. It totals around 100 lines of code, so it isn't a complex beast.
+
+ Please read the ``test_simpleeval.py`` file for other potential gotchas or details. I'm very happy to accept pull requests, suggestions, or other issues. Enjoy!
+
+ .. image:: https://coveralls.io/repos/danthedeckie/simpleeval/badge.png :target: https://coveralls.io/r/danthedeckie/simpleeval
+
+Keywords: eval,simple,expression,parse,ast
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..035b0f7
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,222 @@
+simpleeval (Simple Eval)
+========================
+
+A quick single file library for easily adding evaluatable expressions into python
+projects. Say you want to allow a user to set an alarm volume, which could depend
+on the time of day, alarm level, how many previous alarms had gone off, and if there
+is music playing at the time.
+
+Or if you want to allow simple formulae in a web application, but don't want to
+give full eval() access, or don't want to run in javascript on the client side.
+
+It's deliberately very simple, just a single file you can dump into a project, or import
+from pypi (pip or easy_install).
+
+Internally, it's using the amazing python ``ast`` module to parse the expression, which
+allows very fine control of what is and isn't allowed. It should be completely safe in terms
+of what operations can be performed by the expression.
+
+The only issue I know to be aware of is that you can create an expression which
+takes a long time to evaluate, or which evaluating requires an awful lot of memory,
+which leaves the potential for DOS attacks. There is basic protection against this,
+and you can lock it down further if you desire. (see the `Operators` section below)
+
+You should be aware of this when deploying in a public setting.
+
+The defaults are pretty locked down and basic, and it's very easy to add whatever
+extra specific functionality you need (your own functions, variable/name lookup, etc).
+
+Basic Usage
+-----------
+
+To get very simple evaluating: ::
+
+ from simpleeval import simple_eval
+
+ simple_eval("21 + 21")
+
+returns ``42``.
+
+Expressions can be as complex and convoluted as you want: ::
+
+ simple_eval("21 + 19 / 7 + (8 % 3) ** 9")
+
+returns ``535.714285714``.
+
+You can add your own functions in as well. ::
+
+ simple_eval("square(11)", functions={"square": lambda x: x*x})
+
+returns ``121``.
+
+For more details of working with functions, read further down.
+
+Note:
+~~~~~
+all further examples use ``>>>`` to designate python code, as if you are using the python interactive
+prompt.
+
+Operators
+---------
+You can add operators yourself, using the ``operators`` argument, but these are the defaults:
+
+ +----+------------------------------------+
+ | \+ | add two things. ``x + y`` |
+ | | ``1 + 1`` -> ``2`` |
+ +----+------------------------------------+
+ | \- | subtract two things ``x - y`` |
+ | | ``100 - 1`` -> ``99`` |
+ +----+------------------------------------+
+ | \/ | divide one thing by another |
+ | | ``x / y`` |
+ | | ``100/10`` -> ``10`` |
+ +----+------------------------------------+
+ | \* | multiple one thing by another |
+ | | ``x * y`` |
+ | | ``10 * 10`` -> ``100`` |
+ +----+------------------------------------+
+ |\*\*| 'to the power of' ``x**y`` |
+ | | ``2 ** 10`` -> ``1024`` |
+ +----+------------------------------------+
+ | \% | modulus. (remainder) ``x % y`` |
+ | | ``15 % 4`` -> ``3`` |
+ +----+------------------------------------+
+ | == | equals ``x == y`` |
+ | | ``15 == 4`` -> ``False`` |
+ +----+------------------------------------+
+ | < | Less than. ``x < y`` |
+ | | ``1 < 4`` -> ``True`` |
+ +----+------------------------------------+
+ | > | Greater than. ``x > y`` |
+ | | ``1 > 4`` -> ``False`` |
+ +----+------------------------------------+
+ | <= | Less than or Equal to. ``x <= y`` |
+ | | ``1 < 4`` -> ``True`` |
+ +----+------------------------------------+
+ | >= | Greater or Equal to ``x >= 21`` |
+ | | ``1 >= 4`` -> ``False`` |
+ +----+------------------------------------+
+
+
+The ``^`` operator is notably missing - not because it's hard, but because it is often mistaken for
+a exponent operator, not the bitwise operation that it is in python. It's trivial to add back in again
+if you wish (using the class based evaluator explained below): ::
+
+ >>> import ast
+ >>> import operator
+
+ >>> s = SimpleEval()
+ >>> s.operators[ast.BitXor] = operator.xor
+
+ >>> s.eval("2 ^ 10")
+ 8
+
+Limited Power
+~~~~~~~~~~~~~
+
+Also note, the ``**`` operator has been locked down by default to have a maximum input value
+of ``4000000``, which makes it somewhat harder to make expressions which go on for ever. You
+can change this limit by changing the ``simpleeval.POWER_MAX`` module level value to whatever
+is an appropriate value for you (and the hardware that you're running on) or if you want to
+completely remove all limitations, you can set the ``s.operators[ast.Pow] = operator.pow`` or make
+your own function.
+
+On my computer, ``9**9**5`` evaluates almost instantly, but ``9**9**6`` takes over 30 seconds.
+Since ``9**7`` is ``4782969``, and so over the ``POWER_MAX`` limit, it throws a
+``NumberTooHigh`` exception for you. (Otherwise it would go on for hours, or until the computer
+runs out of memory)
+
+String Safety
+~~~~~~~~~~~~~
+
+There are also limits on string length (100000 characters, ``MAX_STRING_LENGTH``).
+This can be changed if you wish.
+
+If Expressions
+--------------
+
+You can use python style ``if x then y else z`` type expressions: ::
+
+ >>> simple_eval("'equal' if x == y else 'not equal'",
+ names={"x": 1, "y": 2})
+ 'not equal'
+
+which, of course, can be nested: ::
+
+ >>> simple_eval("'a' if 1 == 2 else 'b' if 2 == 3 else 'c'")
+ 'c'
+
+
+Functions
+---------
+
+You can define functions which you'd like the expresssions to have access to: ::
+
+ >>> simple_eval("double(21)", functions={"double": lambda x:x*2})
+ 42
+
+You can define "real" functions to pass in rather than lambdas, of course too, and even re-name them so that expressions can be shorter ::
+
+ >>> def double(x):
+ return x * 2
+ >>> simple_eval("d(100) + double(1)", functions={"d": double, "double":double})
+ 202
+
+Names
+-----
+
+Sometimes it's useful to have variables available, which in python terminology are called 'names'. ::
+
+ >>> simple_eval("a + b", names={"a": 11, "b": 100})
+ 111
+
+You can also hand the handling of names over to a function, if you prefer: ::
+
+ >>> def name_handler(node):
+ return ord(node.id[0].lower(a))-96
+
+ >>> simple_eval('a + b', names=name_handler)
+ 3
+
+That was a bit of a silly example, but you could use this for pulling values from a database or file, say, or doing some kind of caching system.
+
+Creating an Evaluator Class
+---------------------------
+
+Rather than creating a new evaluator each time, if you are doing a lot of evaluations,
+you can create a SimpleEval object, and pass it expressions each time (which should be a bit quicker, and certainly more convienient for some use cases): ::
+
+ s = SimpleEval()
+ s.eval("1 + 1")
+ # and so on...
+
+You can assign / edit the various options of the ``SimpleEval`` object if you want to.
+Either assign them during creation (like the ``simple_eval`` function) ::
+
+ s = SimpleEval(functions={"boo": boo})
+
+or edit them after creation: ::
+
+ s.names['fortytwo'] = 42
+
+this actually means you can modify names (or functions) with functions, if you really feel so inclined: ::
+
+ s = SimpleEval()
+ def set_val(name, value):
+ s.names[name.value] = value.value
+ return value.value
+
+ s.functions = {'set': set_val}
+
+ s.eval("set('age', 111)")
+
+Say. This would allow a certain level of 'scriptyness' if you had these evaluations happening as callbacks in a program. Although you really are reaching the end of what this library is intended for at this stage.
+
+Other...
+--------
+
+This is written using python 2.7, but should be trivial to convert to python3 with the 2to3 converter. It totals around 100 lines of code, so it isn't a complex beast.
+
+Please read the ``test_simpleeval.py`` file for other potential gotchas or details. I'm very happy to accept pull requests, suggestions, or other issues. Enjoy!
+
+.. image:: https://coveralls.io/repos/danthedeckie/simpleeval/badge.png :target: https://coveralls.io/r/danthedeckie/simpleeval
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..e38d9f0
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+simpleeval (0.8.2-1) unstable; urgency=low
+
+ * Initial packaging (Closes: #783188).
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Thu, 23 Apr 2015 19:25:39 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..0f20a62
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,45 @@
+Source: simpleeval
+Section: python
+Priority: optional
+Maintainer: Debian Tryton Maintainers <maintainers at debian.tryton.org>
+Uploaders: Mathias Behrle <mathiasb at m9s.biz>
+Build-Depends:
+ debhelper (>= 9),
+ dh-python (>= 1.20130901-1~),
+ python (>= 2.6.6-3~),
+ python-setuptools,
+ python3,
+ python3-setuptools,
+Standards-Version: 3.9.6
+Homepage: https://github.com/danthedeckie/simpleeval
+Vcs-Browser: http://anonscm.debian.org/gitweb/?p=tryton/simpleeval.git
+Vcs-Git: git://anonscm.debian.org/tryton/simpleeval.git
+X-Python-Version: >= 2.6
+
+Package: python-simpleeval
+Architecture: all
+Depends: python-pkg-resources, ${misc:Depends}, ${python:Depends}
+Description: Simple, safe single expression evaluator library (Python 2)
+ Quick single file library for easily adding evaluatable expressions into
+ Python projects.
+ .
+ Short, easy to use, safe and reasonably extensible expression evaluator.
+ Designed for things like in a website where you want to allow the user to
+ generate a string, or a number from some other input, without allowing full
+ eval() or other unsafe or needlessly complex linguistics.
+ .
+ This package is targeting Python version 2.
+
+Package: python3-simpleeval
+Architecture: all
+Depends: python3-pkg-resources, ${misc:Depends}, ${python3:Depends}
+Description: Simple, safe single expression evaluator library (Python 3)
+ Quick single file library for easily adding evaluatable expressions into
+ Python projects.
+ .
+ Short, easy to use, safe and reasonably extensible expression evaluator.
+ Designed for things like in a website where you want to allow the user to
+ generate a string, or a number from some other input, without allowing full
+ eval() or other unsafe or needlessly complex linguistics.
+ .
+ This package is targeting Python version 3.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..7928ea2
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,28 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+
+Files: *
+Copyright: 2013-2014 Daniel Fairhead
+License: MIT
+
+Files: debian/*
+Copyright: 2015 Mathias Behrle <mathiasb at m9s.biz>
+License: MIT
+
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
diff --git a/debian/gbp.conf b/debian/gbp.conf
new file mode 100644
index 0000000..b6106b4
--- /dev/null
+++ b/debian/gbp.conf
@@ -0,0 +1,12 @@
+# Settings for Debian Tryton Maintainer repositories
+# for usage with git-buildpackage
+
+[DEFAULT]
+debian-branch = debian
+pristine-tar = True
+
+[git-buildpackage]
+ignore-new = True
+# Use export-dir at your discretion to avoid git-buildpackage messing
+# your git repeository
+#export-dir = ../build-area/
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..a0d8f2c
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,11 @@
+#!/usr/bin/make -f
+
+export PYBUILD_NAME := simpleeval
+
+%:
+ dh ${@} --with python2,python3 --buildsystem=pybuild
+
+override_dh_auto_clean:
+ rm -rf $(PACKAGE_NAME).egg-info
+ rm -rf PKG-INFO
+ dh_auto_clean
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..256c226
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,3 @@
+version=3
+opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
+http://pypi.debian.net/simpleeval/simpleeval-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..f319b8b
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,24 @@
+from setuptools import setup
+__version__ = '0.8.2'
+
+setup(
+ name = 'simpleeval',
+ py_modules = ['simpleeval'],
+ version = __version__,
+ description = 'A simple, safe single expression evaluator library.',
+ long_description=open('README.rst','r').read(),
+ author = 'Daniel Fairhead',
+ author_email = 'danthedeckie at gmail.com',
+ url = 'https://github.com/danthedeckie/simpleeval',
+ download_url = 'https://github.com/danthedeckie/simpleeval/tarball/' + __version__,
+ keywords = ['eval', 'simple', 'expression', 'parse', 'ast'],
+ test_suite = 'test_simpleeval',
+ use_2to3 = True,
+ classifiers = ['Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: MIT License',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 3',
+ ],
+ )
diff --git a/simpleeval.egg-info/PKG-INFO b/simpleeval.egg-info/PKG-INFO
new file mode 100644
index 0000000..a321e93
--- /dev/null
+++ b/simpleeval.egg-info/PKG-INFO
@@ -0,0 +1,240 @@
+Metadata-Version: 1.1
+Name: simpleeval
+Version: 0.8.2
+Summary: A simple, safe single expression evaluator library.
+Home-page: https://github.com/danthedeckie/simpleeval
+Author: Daniel Fairhead
+Author-email: danthedeckie at gmail.com
+License: UNKNOWN
+Download-URL: https://github.com/danthedeckie/simpleeval/tarball/0.8.2
+Description: simpleeval (Simple Eval)
+ ========================
+
+ A quick single file library for easily adding evaluatable expressions into python
+ projects. Say you want to allow a user to set an alarm volume, which could depend
+ on the time of day, alarm level, how many previous alarms had gone off, and if there
+ is music playing at the time.
+
+ Or if you want to allow simple formulae in a web application, but don't want to
+ give full eval() access, or don't want to run in javascript on the client side.
+
+ It's deliberately very simple, just a single file you can dump into a project, or import
+ from pypi (pip or easy_install).
+
+ Internally, it's using the amazing python ``ast`` module to parse the expression, which
+ allows very fine control of what is and isn't allowed. It should be completely safe in terms
+ of what operations can be performed by the expression.
+
+ The only issue I know to be aware of is that you can create an expression which
+ takes a long time to evaluate, or which evaluating requires an awful lot of memory,
+ which leaves the potential for DOS attacks. There is basic protection against this,
+ and you can lock it down further if you desire. (see the `Operators` section below)
+
+ You should be aware of this when deploying in a public setting.
+
+ The defaults are pretty locked down and basic, and it's very easy to add whatever
+ extra specific functionality you need (your own functions, variable/name lookup, etc).
+
+ Basic Usage
+ -----------
+
+ To get very simple evaluating: ::
+
+ from simpleeval import simple_eval
+
+ simple_eval("21 + 21")
+
+ returns ``42``.
+
+ Expressions can be as complex and convoluted as you want: ::
+
+ simple_eval("21 + 19 / 7 + (8 % 3) ** 9")
+
+ returns ``535.714285714``.
+
+ You can add your own functions in as well. ::
+
+ simple_eval("square(11)", functions={"square": lambda x: x*x})
+
+ returns ``121``.
+
+ For more details of working with functions, read further down.
+
+ Note:
+ ~~~~~
+ all further examples use ``>>>`` to designate python code, as if you are using the python interactive
+ prompt.
+
+ Operators
+ ---------
+ You can add operators yourself, using the ``operators`` argument, but these are the defaults:
+
+ +----+------------------------------------+
+ | \+ | add two things. ``x + y`` |
+ | | ``1 + 1`` -> ``2`` |
+ +----+------------------------------------+
+ | \- | subtract two things ``x - y`` |
+ | | ``100 - 1`` -> ``99`` |
+ +----+------------------------------------+
+ | \/ | divide one thing by another |
+ | | ``x / y`` |
+ | | ``100/10`` -> ``10`` |
+ +----+------------------------------------+
+ | \* | multiple one thing by another |
+ | | ``x * y`` |
+ | | ``10 * 10`` -> ``100`` |
+ +----+------------------------------------+
+ |\*\*| 'to the power of' ``x**y`` |
+ | | ``2 ** 10`` -> ``1024`` |
+ +----+------------------------------------+
+ | \% | modulus. (remainder) ``x % y`` |
+ | | ``15 % 4`` -> ``3`` |
+ +----+------------------------------------+
+ | == | equals ``x == y`` |
+ | | ``15 == 4`` -> ``False`` |
+ +----+------------------------------------+
+ | < | Less than. ``x < y`` |
+ | | ``1 < 4`` -> ``True`` |
+ +----+------------------------------------+
+ | > | Greater than. ``x > y`` |
+ | | ``1 > 4`` -> ``False`` |
+ +----+------------------------------------+
+ | <= | Less than or Equal to. ``x <= y`` |
+ | | ``1 < 4`` -> ``True`` |
+ +----+------------------------------------+
+ | >= | Greater or Equal to ``x >= 21`` |
+ | | ``1 >= 4`` -> ``False`` |
+ +----+------------------------------------+
+
+
+ The ``^`` operator is notably missing - not because it's hard, but because it is often mistaken for
+ a exponent operator, not the bitwise operation that it is in python. It's trivial to add back in again
+ if you wish (using the class based evaluator explained below): ::
+
+ >>> import ast
+ >>> import operator
+
+ >>> s = SimpleEval()
+ >>> s.operators[ast.BitXor] = operator.xor
+
+ >>> s.eval("2 ^ 10")
+ 8
+
+ Limited Power
+ ~~~~~~~~~~~~~
+
+ Also note, the ``**`` operator has been locked down by default to have a maximum input value
+ of ``4000000``, which makes it somewhat harder to make expressions which go on for ever. You
+ can change this limit by changing the ``simpleeval.POWER_MAX`` module level value to whatever
+ is an appropriate value for you (and the hardware that you're running on) or if you want to
+ completely remove all limitations, you can set the ``s.operators[ast.Pow] = operator.pow`` or make
+ your own function.
+
+ On my computer, ``9**9**5`` evaluates almost instantly, but ``9**9**6`` takes over 30 seconds.
+ Since ``9**7`` is ``4782969``, and so over the ``POWER_MAX`` limit, it throws a
+ ``NumberTooHigh`` exception for you. (Otherwise it would go on for hours, or until the computer
+ runs out of memory)
+
+ String Safety
+ ~~~~~~~~~~~~~
+
+ There are also limits on string length (100000 characters, ``MAX_STRING_LENGTH``).
+ This can be changed if you wish.
+
+ If Expressions
+ --------------
+
+ You can use python style ``if x then y else z`` type expressions: ::
+
+ >>> simple_eval("'equal' if x == y else 'not equal'",
+ names={"x": 1, "y": 2})
+ 'not equal'
+
+ which, of course, can be nested: ::
+
+ >>> simple_eval("'a' if 1 == 2 else 'b' if 2 == 3 else 'c'")
+ 'c'
+
+
+ Functions
+ ---------
+
+ You can define functions which you'd like the expresssions to have access to: ::
+
+ >>> simple_eval("double(21)", functions={"double": lambda x:x*2})
+ 42
+
+ You can define "real" functions to pass in rather than lambdas, of course too, and even re-name them so that expressions can be shorter ::
+
+ >>> def double(x):
+ return x * 2
+ >>> simple_eval("d(100) + double(1)", functions={"d": double, "double":double})
+ 202
+
+ Names
+ -----
+
+ Sometimes it's useful to have variables available, which in python terminology are called 'names'. ::
+
+ >>> simple_eval("a + b", names={"a": 11, "b": 100})
+ 111
+
+ You can also hand the handling of names over to a function, if you prefer: ::
+
+ >>> def name_handler(node):
+ return ord(node.id[0].lower(a))-96
+
+ >>> simple_eval('a + b', names=name_handler)
+ 3
+
+ That was a bit of a silly example, but you could use this for pulling values from a database or file, say, or doing some kind of caching system.
+
+ Creating an Evaluator Class
+ ---------------------------
+
+ Rather than creating a new evaluator each time, if you are doing a lot of evaluations,
+ you can create a SimpleEval object, and pass it expressions each time (which should be a bit quicker, and certainly more convienient for some use cases): ::
+
+ s = SimpleEval()
+ s.eval("1 + 1")
+ # and so on...
+
+ You can assign / edit the various options of the ``SimpleEval`` object if you want to.
+ Either assign them during creation (like the ``simple_eval`` function) ::
+
+ s = SimpleEval(functions={"boo": boo})
+
+ or edit them after creation: ::
+
+ s.names['fortytwo'] = 42
+
+ this actually means you can modify names (or functions) with functions, if you really feel so inclined: ::
+
+ s = SimpleEval()
+ def set_val(name, value):
+ s.names[name.value] = value.value
+ return value.value
+
+ s.functions = {'set': set_val}
+
+ s.eval("set('age', 111)")
+
+ Say. This would allow a certain level of 'scriptyness' if you had these evaluations happening as callbacks in a program. Although you really are reaching the end of what this library is intended for at this stage.
+
+ Other...
+ --------
+
+ This is written using python 2.7, but should be trivial to convert to python3 with the 2to3 converter. It totals around 100 lines of code, so it isn't a complex beast.
+
+ Please read the ``test_simpleeval.py`` file for other potential gotchas or details. I'm very happy to accept pull requests, suggestions, or other issues. Enjoy!
+
+ .. image:: https://coveralls.io/repos/danthedeckie/simpleeval/badge.png :target: https://coveralls.io/r/danthedeckie/simpleeval
+
+Keywords: eval,simple,expression,parse,ast
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
diff --git a/simpleeval.egg-info/SOURCES.txt b/simpleeval.egg-info/SOURCES.txt
new file mode 100644
index 0000000..0547ed6
--- /dev/null
+++ b/simpleeval.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+MANIFEST.in
+README.rst
+setup.py
+simpleeval.py
+simpleeval.egg-info/PKG-INFO
+simpleeval.egg-info/SOURCES.txt
+simpleeval.egg-info/dependency_links.txt
+simpleeval.egg-info/top_level.txt
\ No newline at end of file
diff --git a/simpleeval.egg-info/dependency_links.txt b/simpleeval.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/simpleeval.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/simpleeval.egg-info/top_level.txt b/simpleeval.egg-info/top_level.txt
new file mode 100644
index 0000000..49a7c04
--- /dev/null
+++ b/simpleeval.egg-info/top_level.txt
@@ -0,0 +1 @@
+simpleeval
diff --git a/simpleeval.py b/simpleeval.py
new file mode 100644
index 0000000..67af226
--- /dev/null
+++ b/simpleeval.py
@@ -0,0 +1,282 @@
+'''
+SimpleEval - (C) 2013/2014 Daniel Fairhead
+-------------------------------------
+
+An short, easy to use, safe and reasonably extensible expression evaluator.
+Designed for things like in a website where you want to allow the user to
+generate a string, or a number from some other input, without allowing full
+eval() or other unsafe or needlessly complex linguistics.
+
+-------------------------------------
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+-------------------------------------
+
+Initial idea copied from J.F. Sebastian on Stack Overflow
+( http://stackoverflow.com/a/9558001/1973500 ) with
+modifications and many improvments.
+
+-------------------------------------
+Usage:
+
+>>> s = SimpleEval()
+>>> s.eval("20 + 30")
+50
+
+You can add your own functions easily too:
+
+if file.txt contents is "11"
+
+>>> def get_file():
+ with open("file.txt",'r') as f:
+ return f.read()
+
+ s.functions["get_file"] = get_file
+ s.eval("int(get_file()) + 31")
+42
+
+For more information, see the full package documentation on pypi, or the github
+repo.
+
+-----------
+
+If you don't need to re-use the evaluator (with it's names, functions, etc),
+then you can use the simple_eval() function:
+
+>>> simple_eval("21 + 19")
+40
+
+You can pass names, operators and functions to the simple_eval function as
+well:
+
+>>> simple_eval("40 + two", names={"two": 2})
+42
+
+'''
+
+import ast
+import operator as op
+from random import random
+
+########################################
+# Module wide 'globals'
+
+MAX_STRING_LENGTH = 100000
+MAX_POWER = 4000000 # highest exponent
+
+########################################
+# Exceptions:
+
+class InvalidExpression(Exception):
+ ''' Generic Exception '''
+ pass
+
+class FunctionNotDefined(InvalidExpression):
+ ''' sorry! That function isn't defined! '''
+ def __init__(self, func_name, expression):
+ self.message = "Function '{0}' not defined," \
+ " for expression '{1}'.".format( func_name, expression)
+ self.func_name = func_name
+ self.expression = expression
+
+ # pylint: disable=bad-super-call
+ super(InvalidExpression, self).__init__(self.message)
+
+class NameNotDefined(InvalidExpression):
+ ''' a name isn't defined. '''
+ def __init__(self, name, expression):
+ self.message = "'{0}' is not defined for expression '{1}'".format(
+ name, expression)
+ self.name = name
+ self.expression = expression
+
+ # pylint: disable=bad-super-call
+ super(InvalidExpression, self).__init__(self.message)
+
+class FeatureNotAvailable(InvalidExpression):
+ ''' What you're trying to do is not allowed. '''
+ pass
+
+class NumberTooHigh(InvalidExpression):
+ ''' Sorry! That number is too high. I don't want to spend the
+ next 10 years evaluating this expression! '''
+ pass
+
+class StringTooLong(InvalidExpression):
+ ''' That string is **way** too long, baby. '''
+ pass
+
+########################################
+# Default simple functions to include:
+
+def random_int(top):
+ ''' return a random int below <top> '''
+ return int(random() * top)
+
+def safe_power(a, b): # pylint: disable=invalid-name
+ ''' a limited exponent/to-the-power-of function, for safety reasons '''
+ if abs(a) > MAX_POWER or abs(b) > MAX_POWER:
+ raise NumberTooHigh("Sorry! I don't want to evaluate {0} ** {1}"
+ .format(a, b))
+ return a ** b
+
+def safe_mult(a, b): # pylint: disable=invalid-name
+ ''' limit the number of times a string can be repeated... '''
+ if isinstance(a, str) or isinstance(b, str):
+ if isinstance(a, int) and a*len(b) > MAX_STRING_LENGTH:
+ raise StringTooLong("Sorry, a string that long is not allowed")
+ elif isinstance(b, int) and b*len(a) > MAX_STRING_LENGTH:
+ raise StringTooLong("Sorry, a string that long is not allowed")
+
+ return a * b
+
+def safe_add(a, b): # pylint: disable=invalid-name
+ ''' string length limit again '''
+ if isinstance(a, str) and isinstance(b, str):
+ if len(a) + len(b) > MAX_STRING_LENGTH:
+ raise StringTooLong("Sorry, adding those two strings would"
+ " make a too long string.")
+ return a + b
+
+
+########################################
+# Defaults for the evaluator:
+
+DEFAULT_OPERATORS = {ast.Add: safe_add, ast.Sub: op.sub, ast.Mult: safe_mult,
+ ast.Div: op.truediv, ast.Pow: safe_power, ast.Mod: op.mod,
+ ast.Eq: op.eq, ast.Gt: op.gt, ast.Lt: op.lt,
+ ast.GtE: op.ge, ast.LtE: op.le, ast.USub: op.neg,
+ ast.UAdd: op.pos}
+
+DEFAULT_FUNCTIONS = {"rand": random, "randint": random_int,
+ "int": int, "float": float, "str": unicode}
+
+DEFAULT_NAMES = {"True": True, "False": False}
+
+########################################
+# And the actual evaluator:
+
+class SimpleEval(object): # pylint: disable=too-few-public-methods
+ ''' A very simple expression parser.
+ >>> s = SimpleEval()
+ >>> s.eval("20 + 30 - ( 10 * 5)")
+ 0
+ '''
+ expr = ""
+
+ def __init__(self, operators=None, functions=None, names=None):
+ '''
+ Create the evaluator instance. Set up valid operators (+,-, etc)
+ functions (add, random, get_val, whatever) and names. '''
+
+ if not operators:
+ operators = DEFAULT_OPERATORS
+ if not functions:
+ functions = DEFAULT_FUNCTIONS
+ if not names:
+ names = DEFAULT_NAMES
+
+ self.operators = operators
+ self.functions = functions
+ self.names = names
+
+ def eval(self, expr):
+ ''' evaluate an expresssion, using the operators, functions and
+ names previously set up. '''
+
+ # set a copy of the expression aside, so we can give nice errors...
+
+ self.expr = expr
+
+ # and evaluate:
+ return self._eval(ast.parse(expr).body[0].value)
+
+ # pylint: disable=too-many-return-statements, too-many-branches
+ def _eval(self, node):
+ ''' The internal eval function used on each node in the parsed tree. '''
+
+ # literals:
+
+ if isinstance(node, ast.Num): # <number>
+ return node.n
+ elif isinstance(node, ast.Str): # <string>
+ if len(node.s) > MAX_STRING_LENGTH:
+ raise StringTooLong("{0} is too long!"
+ " ({1}, when {2} is max)".format(
+ node.id, len(node.s), MAX_STRING_LENGTH))
+ return node.s
+
+ # python 3 compatibility:
+
+ elif (hasattr(ast, 'NameConstant') and
+ isinstance(node, ast.NameConstant)): # <bool>
+ return node.value
+
+ # operators, functions, etc:
+
+ elif isinstance(node, ast.UnaryOp): # - and + etc.
+ return self.operators[type(node.op)](self._eval(node.operand))
+ elif isinstance(node, ast.BinOp): # <left> <operator> <right>
+ return self.operators[type(node.op)](self._eval(node.left),
+ self._eval(node.right))
+ elif isinstance(node, ast.BoolOp): # and & or...
+ if isinstance(node.op, ast.And):
+ return all((self._eval(v) for v in node.values))
+ elif isinstance(node.op, ast.Or):
+ return any((self._eval(v) for v in node.values))
+ elif isinstance(node, ast.Compare): # 1 < 2, a == b...
+ return self.operators[type(node.ops[0])](self._eval(node.left),
+ self._eval(node.comparators[0]))
+ elif isinstance(node, ast.IfExp): # x if y else z
+ return self._eval(node.body) if self._eval(node.test) \
+ else self._eval(node.orelse)
+ elif isinstance(node, ast.Call): # function...
+ try:
+ return self.functions[node.func.id](*(self._eval(a)
+ for a in node.args))
+ except KeyError:
+ raise FunctionNotDefined(node.func.id, self.expr)
+
+ # variables/names:
+
+ elif isinstance(node, ast.Name): # a, b, c...
+ try:
+ if isinstance(self.names, dict):
+ return self.names[node.id]
+ elif callable(self.names):
+ return self.names(node)
+ else:
+ raise KeyError('undefined name')
+ except KeyError:
+ raise NameNotDefined(node.id, self.expr)
+
+ elif isinstance(node, ast.Subscript): # b[1]
+ return self._eval(node.value)[self._eval(node.slice.value)]
+
+ else:
+ raise FeatureNotAvailable("Sorry, {0} is not available in this "
+ "evaluator".format(type(node).__name__ ))
+
+def simple_eval(expr, operators=None, functions=None, names=None):
+ ''' Simply evaluate an expresssion '''
+ s = SimpleEval(operators=operators,
+ functions=functions,
+ names=names)
+ return s.eval(expr)
--
simpleeval
More information about the tryton-debian-vcs
mailing list