diff --git a/leval/evaluator.py b/leval/evaluator.py index 55ee2c3..2afcea6 100644 --- a/leval/evaluator.py +++ b/leval/evaluator.py @@ -22,7 +22,12 @@ DEFAULT_ALLOWED_CONTAINER_TYPES = frozenset((tuple, set)) DEFAULT_ALLOWED_CONSTANT_TYPES = frozenset( - (str, int, float, NoneType), + ( + str, + int, # NB: this implicitly includes bool + float, + NoneType, + ), ) diff --git a/leval/excs.py b/leval/excs.py index 2e13b38..9a0773b 100644 --- a/leval/excs.py +++ b/leval/excs.py @@ -29,6 +29,10 @@ class InvalidOperands(InvalidOperation): pass +class InvalidAttribute(InvalidOperation): + pass + + class NoSuchValue(NameError, EvaluatorError): pass diff --git a/leval/utils.py b/leval/utils.py index 8e94da4..94452a0 100644 --- a/leval/utils.py +++ b/leval/utils.py @@ -1,7 +1,7 @@ import ast from typing import Tuple -from leval.excs import InvalidOperation +from leval.excs import InvalidAttribute def expand_name(node: ast.Attribute) -> Tuple[str, ...]: @@ -18,8 +18,13 @@ def walk_attr(kid): walk_attr(kid.value) elif isinstance(kid, ast.Name): attr_bits.append(kid.id) + elif isinstance(kid, ast.Constant): + raise InvalidAttribute( + f"Accessing attributes of constants ({kid}) is not allowed", + node=node, + ) else: - raise InvalidOperation( # pragma: no cover + raise InvalidAttribute( # pragma: no cover f"Unsupported attribute structure in {node}", node=node, ) diff --git a/leval_tests/test_leval.py b/leval_tests/test_leval.py index 60f5ced..f95e8f0 100644 --- a/leval_tests/test_leval.py +++ b/leval_tests/test_leval.py @@ -5,6 +5,7 @@ from leval.evaluator import Evaluator from leval.excs import ( + InvalidAttribute, InvalidOperands, InvalidOperation, NoSuchFunction, @@ -81,7 +82,10 @@ ), ("Can't access weird methods off valid names", "abs.__class__", NoSuchValue), ("Arbitrary Python code is not allowed", "if x > a:\n hello()", SyntaxError), - ("Can't access attributes off constants", "(3).__class__", InvalidOperation), + ("Can't access numeric constants' attributes", "(3).__class__", InvalidAttribute), + ("Can't access Nones' attributes", "None.__class__", InvalidAttribute), + ("Can't access Falses' attributes", "False.__class__", InvalidAttribute), + ("Can't access Trues' attributes", "True.__class__", InvalidAttribute), ( "Walruses aren't allowed", "(a := 3) + 8",