[tryton-debian-vcs] simpleeval branch upstream created. 5e7592badb01ccfd119b463a9f91e95c2938529b
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 upstream branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/simpleeval.git;a=commitdiff;h=5e7592badb01ccfd119b463a9f91e95c2938529b
commit 5e7592badb01ccfd119b463a9f91e95c2938529b
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/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