Browse Source

added evaluate_test.py

Fabian Peter Hammerle 5 years ago
parent
commit
f6b204aa88
3 changed files with 73 additions and 0 deletions
  1. 35 0
      acpi_backlight/evaluate.py
  2. 37 0
      acpi_backlight/evaluate_test.py
  3. 1 0
      setup.py

+ 35 - 0
acpi_backlight/evaluate.py

@@ -0,0 +1,35 @@
+# pylint: disable=missing-docstring
+
+import ast
+import operator as python_operator
+
+
+_OPERATORS = {
+    ast.Add: python_operator.add,
+    ast.Div: python_operator.truediv,
+    ast.Mult: python_operator.mul,
+    ast.Pow: python_operator.pow,
+    ast.Sub: python_operator.sub,
+    ast.USub: python_operator.neg,
+}
+
+
+def _evaluate(node, names):
+    if isinstance(node, ast.Num):
+        return node.n
+    elif isinstance(node, ast.Name):
+        return names[node.id]
+    elif isinstance(node, ast.UnaryOp):
+        operand = _evaluate(node.operand, names=names)
+        return _OPERATORS[type(node.op)](operand)
+    elif isinstance(node, ast.BinOp):
+        operand_left = _evaluate(node.left, names=names)
+        operand_right = _evaluate(node.right, names=names)
+        return _OPERATORS[type(node.op)](operand_left, operand_right)
+    else:
+        raise Exception(node)
+
+
+def evaluate_expression(expr_str, names):
+    expr = ast.parse(expr_str, mode='eval')
+    return _evaluate(expr.body, names=names)

+ 37 - 0
acpi_backlight/evaluate_test.py

@@ -0,0 +1,37 @@
+# pylint: disable=missing-docstring
+
+import pytest
+
+from acpi_backlight.evaluate import evaluate_expression
+
+
+@pytest.mark.parametrize(('expr_str', 'names', 'expected'), [
+    ('0', {}, 0),
+    ('0.0', {}, 0),
+    ('1.0', {}, 1),
+    ('-1', {}, -1),
+    ('0.1 + 0.2', {}, 0.3),
+    ('0.3 - 0.2', {}, 0.1),
+    ('0.2 * 3', {}, 0.6),
+    ('0.6 / 3', {}, 0.2),
+    ('0.6 / 3 + 0.1', {}, 0.3),
+    ('(0.6 - 0.2) / 2', {}, 0.2),
+    ('b', {'b': 0.4}, 0.4),
+    ('-b', {'b': 0.3}, -0.3),
+    ('0.1 + b', {'b': 0.2}, 0.3),
+])
+def test_evaluate_expression(expr_str, names, expected):
+    assert expected == pytest.approx(evaluate_expression(expr_str, names))
+
+
+@pytest.mark.parametrize('expr_str', [
+    'read("/proc/cpuinfo")',
+    'os.exit(42)',
+    'os.system("echo evil")',
+    '0.__class__',
+    'None.__class__',
+    'eval("1")',
+])
+def test_evaluate_expression_fail(expr_str):
+    with pytest.raises(Exception):
+        evaluate_expression(expr_str, {})

+ 1 - 0
setup.py

@@ -10,6 +10,7 @@ setuptools.setup(
     url='https://github.com/fphammerle/acpi-backlight',
     packages=['acpi_backlight'],
     setup_requires=['setuptools_scm'],
+    tests_require=['pytest'],
     classifiers=[
         "License :: OSI Approved :: MIT License",
         "Operating System :: POSIX :: Linux",