From 44619a2025bbc4aab16aec23d618b5cafb55b2ce Mon Sep 17 00:00:00 2001 From: barteg Date: Mon, 13 Oct 2025 14:45:27 +0200 Subject: [PATCH 01/11] testing lukasz skills --- feature1/lukasz.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 feature1/lukasz.txt diff --git a/feature1/lukasz.txt b/feature1/lukasz.txt new file mode 100644 index 0000000..e69de29 From 2bd019bcfca73641b5895ffade58a73e9b5176e3 Mon Sep 17 00:00:00 2001 From: LuckyLuke255 <105583948+LuckyLuke255@users.noreply.github.com> Date: Mon, 13 Oct 2025 14:48:34 +0200 Subject: [PATCH 02/11] Update lukasz.txt --- feature1/lukasz.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/feature1/lukasz.txt b/feature1/lukasz.txt index e69de29..f91c576 100644 --- a/feature1/lukasz.txt +++ b/feature1/lukasz.txt @@ -0,0 +1 @@ +good From 9e4180cefeaacf37d917b16a834eef8cbb5ec205 Mon Sep 17 00:00:00 2001 From: LuckyLuke255 <105583948+LuckyLuke255@users.noreply.github.com> Date: Mon, 13 Oct 2025 14:48:56 +0200 Subject: [PATCH 03/11] Update lukasz.txt From 343be83806fd5a1b01c5cfc9e6a5b9f3c94f08e5 Mon Sep 17 00:00:00 2001 From: LuckyLuke255 <105583948+LuckyLuke255@users.noreply.github.com> Date: Mon, 20 Oct 2025 13:42:47 +0200 Subject: [PATCH 04/11] Create project.py --- feature1/project.py | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 feature1/project.py diff --git a/feature1/project.py b/feature1/project.py new file mode 100644 index 0000000..610a89e --- /dev/null +++ b/feature1/project.py @@ -0,0 +1,58 @@ +import ast, operator, math, sys + +_ops = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, + ast.FloorDiv: operator.floordiv, + ast.Mod: operator.mod, + ast.Pow: operator.pow, + ast.USub: operator.neg, + ast.UAdd: operator.pos, +} + +_funcs = {k: getattr(math, k) for k in ( + "sin","cos","tan","asin","acos","atan","sqrt","log","log10","exp", + "fabs","factorial","degrees","radians","ceil","floor","gamma","lgamma" +)} +_consts = {"pi": math.pi, "e": math.e, "tau": math.tau, "inf": math.inf} + +def _eval(node): + if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): + return node.value + if isinstance(node, ast.Num): + return node.n + if isinstance(node, ast.UnaryOp) and type(node.op) in _ops: + return _ops[type(node.op)](_eval(node.operand)) + if isinstance(node, ast.BinOp) and type(node.op) in _ops: + return _ops[type(node.op)](_eval(node.left), _eval(node.right)) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.keywords == []: + fname = node.func.id + if fname in _funcs: + return _funcs[fname](*[_eval(a) for a in node.args]) + if isinstance(node, ast.Name) and node.id in _consts: + return _consts[node.id] + raise ValueError("invalid expression") + +def calculate(expr): + tree = ast.parse(expr, mode="eval") + return _eval(tree.body) + +def main(): + if len(sys.argv) > 1: + print(calculate(" ".join(sys.argv[1:]))) + return + while True: + try: + s = input("> ").strip() + if s.lower() in {"exit","quit"}: + break + if not s: + continue + print(calculate(s)) + except Exception as e: + print(f"Error: {e}") + +if __name__ == "__main__": + main() From f06d6763d141b53f591ed7bdd57b184e63334470 Mon Sep 17 00:00:00 2001 From: barteg Date: Mon, 20 Oct 2025 13:53:35 +0200 Subject: [PATCH 05/11] feat: Add calculator mode selection to project.py --- feature1/project.py | 56 ++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/feature1/project.py b/feature1/project.py index 610a89e..8cb9183 100644 --- a/feature1/project.py +++ b/feature1/project.py @@ -1,6 +1,6 @@ import ast, operator, math, sys -_ops = { +_SCIENTIFIC_OPS = { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, @@ -12,36 +12,60 @@ ast.UAdd: operator.pos, } -_funcs = {k: getattr(math, k) for k in ( +_SCIENTIFIC_FUNCS = {k: getattr(math, k) for k in ( "sin","cos","tan","asin","acos","atan","sqrt","log","log10","exp", "fabs","factorial","degrees","radians","ceil","floor","gamma","lgamma" )} -_consts = {"pi": math.pi, "e": math.e, "tau": math.tau, "inf": math.inf} +_SCIENTIFIC_CONSTS = {"pi": math.pi, "e": math.e, "tau": math.tau, "inf": math.inf} -def _eval(node): +_SIMPLE_OPS = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, +} +_SIMPLE_FUNCS = {} +_SIMPLE_CONSTS = {} + +def _eval(node, ops, funcs, consts): if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): return node.value if isinstance(node, ast.Num): return node.n - if isinstance(node, ast.UnaryOp) and type(node.op) in _ops: - return _ops[type(node.op)](_eval(node.operand)) - if isinstance(node, ast.BinOp) and type(node.op) in _ops: - return _ops[type(node.op)](_eval(node.left), _eval(node.right)) + if isinstance(node, ast.UnaryOp) and type(node.op) in ops: + return ops[type(node.op)](_eval(node.operand, ops, funcs, consts)) + if isinstance(node, ast.BinOp) and type(node.op) in ops: + return ops[type(node.op)](_eval(node.left, ops, funcs, consts), _eval(node.right, ops, funcs, consts)) if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.keywords == []: fname = node.func.id - if fname in _funcs: - return _funcs[fname](*[_eval(a) for a in node.args]) - if isinstance(node, ast.Name) and node.id in _consts: - return _consts[node.id] + if fname in funcs: + return funcs[fname](*[_eval(a, ops, funcs, consts) for a in node.args]) + if isinstance(node, ast.Name) and node.id in consts: + return consts[node.id] raise ValueError("invalid expression") -def calculate(expr): +def calculate(expr, ops, funcs, consts): tree = ast.parse(expr, mode="eval") - return _eval(tree.body) + return _eval(tree.body, ops, funcs, consts) def main(): + mode = input("Choose calculator mode (simple/scientific): ").lower() + if mode == "simple": + current_ops = _SIMPLE_OPS + current_funcs = _SIMPLE_FUNCS + current_consts = _SIMPLE_CONSTS + elif mode == "scientific": + current_ops = _SCIENTIFIC_OPS + current_funcs = _SCIENTIFIC_FUNCS + current_consts = _SCIENTIFIC_CONSTS + else: + print("Invalid mode. Defaulting to scientific.") + current_ops = _SCIENTIFIC_OPS + current_funcs = _SCIENTIFIC_FUNCS + current_consts = _SCIENTIFIC_CONSTS + if len(sys.argv) > 1: - print(calculate(" ".join(sys.argv[1:]))) + print(calculate(" ".join(sys.argv[1:]), current_ops, current_funcs, current_consts)) return while True: try: @@ -50,7 +74,7 @@ def main(): break if not s: continue - print(calculate(s)) + print(calculate(s, current_ops, current_funcs, current_consts)) except Exception as e: print(f"Error: {e}") From c3ff5efa6c66f82a6cffcee733232bdadaedfe57 Mon Sep 17 00:00:00 2001 From: barteg Date: Mon, 20 Oct 2025 14:26:18 +0200 Subject: [PATCH 06/11] feat: Add option to reuse previous values (ans) --- feature1/project.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/feature1/project.py b/feature1/project.py index 8cb9183..dcb39cf 100644 --- a/feature1/project.py +++ b/feature1/project.py @@ -27,26 +27,29 @@ _SIMPLE_FUNCS = {} _SIMPLE_CONSTS = {} -def _eval(node, ops, funcs, consts): +def _eval(node, ops, funcs, consts, last_result): if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): return node.value if isinstance(node, ast.Num): return node.n if isinstance(node, ast.UnaryOp) and type(node.op) in ops: - return ops[type(node.op)](_eval(node.operand, ops, funcs, consts)) + return ops[type(node.op)](_eval(node.operand, ops, funcs, consts, last_result)) if isinstance(node, ast.BinOp) and type(node.op) in ops: - return ops[type(node.op)](_eval(node.left, ops, funcs, consts), _eval(node.right, ops, funcs, consts)) + return ops[type(node.op)](_eval(node.left, ops, funcs, consts, last_result), _eval(node.right, ops, funcs, consts, last_result)) if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.keywords == []: fname = node.func.id if fname in funcs: - return funcs[fname](*[_eval(a, ops, funcs, consts) for a in node.args]) - if isinstance(node, ast.Name) and node.id in consts: - return consts[node.id] + return funcs[fname](*[_eval(a, ops, funcs, consts, last_result) for a in node.args]) + if isinstance(node, ast.Name): + if node.id in consts: + return consts[node.id] + if node.id == "ans": + return last_result raise ValueError("invalid expression") -def calculate(expr, ops, funcs, consts): +def calculate(expr, ops, funcs, consts, last_result): tree = ast.parse(expr, mode="eval") - return _eval(tree.body, ops, funcs, consts) + return _eval(tree.body, ops, funcs, consts, last_result) def main(): mode = input("Choose calculator mode (simple/scientific): ").lower() @@ -64,8 +67,15 @@ def main(): current_funcs = _SCIENTIFIC_FUNCS current_consts = _SCIENTIFIC_CONSTS + last_result = 0 + if len(sys.argv) > 1: - print(calculate(" ".join(sys.argv[1:]), current_ops, current_funcs, current_consts)) + try: + result = calculate(" ".join(sys.argv[1:]), current_ops, current_funcs, current_consts, last_result) + print(result) + last_result = result + except Exception as e: + print(f"Error: {e}") return while True: try: @@ -74,7 +84,9 @@ def main(): break if not s: continue - print(calculate(s, current_ops, current_funcs, current_consts)) + result = calculate(s, current_ops, current_funcs, current_consts, last_result) + print(result) + last_result = result except Exception as e: print(f"Error: {e}") From 9174447f4b1de3446b6ce62ad54dc4f54df0797b Mon Sep 17 00:00:00 2001 From: LuckyLuke255 <105583948+LuckyLuke255@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:44:47 +0100 Subject: [PATCH 07/11] Create tests_project.py --- feature1/tests_project.py | 180 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 feature1/tests_project.py diff --git a/feature1/tests_project.py b/feature1/tests_project.py new file mode 100644 index 0000000..0b371d9 --- /dev/null +++ b/feature1/tests_project.py @@ -0,0 +1,180 @@ +import ast, operator, math, sys + +_MAX_EXPR_LEN = 2000 +_MAX_AST_DEPTH = 64 +_MAX_INT_BITS = 1_000_000 +_MAX_INT_EXP = 10_000 +_MAX_FLOAT_EXP = 1_000 +_ALLOW_COMPLEX = False + +def _ast_depth(node, d=0): + return max([d] + [_ast_depth(c, d+1) for c in ast.iter_child_nodes(node)]) + +def _assert_numeric_limits(x): + if isinstance(x, bool): + raise ValueError("invalid expression") + if isinstance(x, int): + if x.bit_length() > _MAX_INT_BITS: + raise OverflowError("integer too large") + elif isinstance(x, float): + pass + elif isinstance(x, complex): + if not _ALLOW_COMPLEX: + raise ValueError("complex results are not supported") + else: + raise ValueError("invalid expression") + return x + +def _safe_pow(a, b): + if (isinstance(a, complex) or isinstance(b, complex)) and not _ALLOW_COMPLEX: + raise ValueError("complex results are not supported") + if isinstance(b, int): + if abs(b) > _MAX_INT_EXP: + raise OverflowError("exponent too large") + elif isinstance(b, float): + if abs(b) > _MAX_FLOAT_EXP: + raise OverflowError("exponent too large") + r = operator.pow(a, b) + return _assert_numeric_limits(r) + +def _safe_div(a, b): + if b == 0: + raise ZeroDivisionError("division by zero") + return operator.truediv(a, b) + +def _safe_floordiv(a, b): + if b == 0: + raise ZeroDivisionError("division by zero") + return operator.floordiv(a, b) + +def _safe_mod(a, b): + if b == 0: + raise ZeroDivisionError("modulo by zero") + return operator.mod(a, b) + +_SCIENTIFIC_OPS = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: _safe_div, + ast.FloorDiv: _safe_floordiv, + ast.Mod: _safe_mod, + ast.Pow: _safe_pow, + ast.USub: operator.neg, + ast.UAdd: operator.pos, +} + +_SCIENTIFIC_FUNCS = {k: getattr(math, k) for k in ( + "sin","cos","tan","asin","acos","atan","sqrt","log","log10","exp", + "fabs","factorial","degrees","radians","ceil","floor","gamma","lgamma" +)} +_SCIENTIFIC_CONSTS = {"pi": math.pi, "e": math.e, "tau": math.tau, "inf": math.inf, "nan": math.nan} + +_SIMPLE_OPS = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: _safe_div, +} +_SIMPLE_FUNCS = {} +_SIMPLE_CONSTS = {} + +_ALLOWED_AST_NODES = ( + ast.Expression, ast.Constant, ast.Num, + ast.UnaryOp, ast.BinOp, + ast.Call, ast.Name, ast.Load +) + +def _validate_ast(tree): + for node in ast.walk(tree): + if not isinstance(node, _ALLOWED_AST_NODES): + raise ValueError("invalid expression") + if _ast_depth(tree) > _MAX_AST_DEPTH: + raise ValueError("expression too deep") + +def _eval(node, ops, funcs, consts, last_result): + if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): + return _assert_numeric_limits(node.value) + if isinstance(node, ast.Num): + return _assert_numeric_limits(node.n) + if isinstance(node, ast.UnaryOp) and type(node.op) in ops: + v = _eval(node.operand, ops, funcs, consts, last_result) + return _assert_numeric_limits(ops[type(node.op)](v)) + if isinstance(node, ast.BinOp) and type(node.op) in ops: + l = _eval(node.left, ops, funcs, consts, last_result) + r = _eval(node.right, ops, funcs, consts, last_result) + return _assert_numeric_limits(ops[type(node.op)](l, r)) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.keywords == []: + fname = node.func.id + if fname in funcs: + args = [_eval(a, ops, funcs, consts, last_result) for a in node.args] + if fname == "factorial": + if not (len(args) == 1 and isinstance(args[0], int) and args[0] >= 0 and args[0] <= 100000): + raise OverflowError("factorial argument out of allowed range") + r = funcs[fname](*args) + return _assert_numeric_limits(r) + if isinstance(node, ast.Name): + if node.id in consts: + return _assert_numeric_limits(consts[node.id]) + if node.id == "ans": + return _assert_numeric_limits(last_result) + raise ValueError("invalid expression") + +def calculate(expr, ops, funcs, consts, last_result): + if not isinstance(expr, str) or not expr.strip(): + raise ValueError("invalid expression") + if len(expr) > _MAX_EXPR_LEN: + raise ValueError("expression too long") + tree = ast.parse(expr, mode="eval") + _validate_ast(tree) + return _eval(tree.body, ops, funcs, consts, last_result) + +def _friendly_error(e: Exception) -> str: + if isinstance(e, ZeroDivisionError): + return "Division by zero." + if isinstance(e, OverflowError): + return str(e) or "Computation too large." + if isinstance(e, ValueError): + msg = str(e) or "invalid expression" + if "math domain error" in msg: + return "Math domain error (e.g., sqrt of negative, log of non-positive)." + if "invalid" in msg: + return "Invalid expression." + return msg + if isinstance(e, TypeError): + return "Invalid argument types or wrong number of arguments." + return f"Error: {e}" + +def main(): + mode = input("Choose calculator mode (simple/scientific): ").lower() + if mode == "simple": + current_ops = _SIMPLE_OPS; current_funcs = _SIMPLE_FUNCS; current_consts = _SIMPLE_CONSTS + elif mode == "scientific": + current_ops = _SCIENTIFIC_OPS; current_funcs = _SCIENTIFIC_FUNCS; current_consts = _SCIENTIFIC_CONSTS + else: + print("Invalid mode. Defaulting to scientific.") + current_ops = _SCIENTIFIC_OPS; current_funcs = _SCIENTIFIC_FUNCS; current_consts = _SCIENTIFIC_CONSTS + last_result = 0 + if len(sys.argv) > 1: + try: + result = calculate(" ".join(sys.argv[1:]), current_ops, current_funcs, current_consts, last_result) + print(result) + last_result = result + except Exception as e: + print(_friendly_error(e)) + return + while True: + try: + s = input("> ").strip() + if s.lower() in {"exit","quit"}: + break + if not s: + continue + result = calculate(s, current_ops, current_funcs, current_consts, last_result) + print(result) + last_result = result + except Exception as e: + print(_friendly_error(e)) + +if __name__ == "__main__": + main() From 722fce2d6f24dbadfc4b3ad226ec86e560a36f5a Mon Sep 17 00:00:00 2001 From: LuckyLuke255 <105583948+LuckyLuke255@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:51:24 +0100 Subject: [PATCH 08/11] Update tests_project.py --- feature1/tests_project.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/feature1/tests_project.py b/feature1/tests_project.py index 0b371d9..1476f63 100644 --- a/feature1/tests_project.py +++ b/feature1/tests_project.py @@ -80,9 +80,10 @@ def _safe_mod(a, b): _SIMPLE_CONSTS = {} _ALLOWED_AST_NODES = ( - ast.Expression, ast.Constant, ast.Num, + ast.Expression, ast.Constant, ast.UnaryOp, ast.BinOp, - ast.Call, ast.Name, ast.Load + ast.Call, ast.Name, ast.Load, + ast.operator, ast.unaryop, ) def _validate_ast(tree): @@ -95,8 +96,6 @@ def _validate_ast(tree): def _eval(node, ops, funcs, consts, last_result): if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): return _assert_numeric_limits(node.value) - if isinstance(node, ast.Num): - return _assert_numeric_limits(node.n) if isinstance(node, ast.UnaryOp) and type(node.op) in ops: v = _eval(node.operand, ops, funcs, consts, last_result) return _assert_numeric_limits(ops[type(node.op)](v)) @@ -109,7 +108,7 @@ def _eval(node, ops, funcs, consts, last_result): if fname in funcs: args = [_eval(a, ops, funcs, consts, last_result) for a in node.args] if fname == "factorial": - if not (len(args) == 1 and isinstance(args[0], int) and args[0] >= 0 and args[0] <= 100000): + if not (len(args) == 1 and isinstance(args[0], int) and 0 <= args[0] <= 100000): raise OverflowError("factorial argument out of allowed range") r = funcs[fname](*args) return _assert_numeric_limits(r) From 15f5e7d08918aec066114509037f341c26bcd027 Mon Sep 17 00:00:00 2001 From: LuckyLuke255 <105583948+LuckyLuke255@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:05:29 +0100 Subject: [PATCH 09/11] Update tests_project.py --- feature1/tests_project.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/feature1/tests_project.py b/feature1/tests_project.py index 1476f63..3d11ee5 100644 --- a/feature1/tests_project.py +++ b/feature1/tests_project.py @@ -7,6 +7,9 @@ _MAX_FLOAT_EXP = 1_000 _ALLOW_COMPLEX = False +_total_ops = 0 +_success_ops = 0 + def _ast_depth(node, d=0): return max([d] + [_ast_depth(c, d+1) for c in ast.iter_child_nodes(node)]) @@ -26,6 +29,8 @@ def _assert_numeric_limits(x): return x def _safe_pow(a, b): + global _total_ops + _total_ops += 1 if (isinstance(a, complex) or isinstance(b, complex)) and not _ALLOW_COMPLEX: raise ValueError("complex results are not supported") if isinstance(b, int): @@ -38,16 +43,22 @@ def _safe_pow(a, b): return _assert_numeric_limits(r) def _safe_div(a, b): + global _total_ops + _total_ops += 1 if b == 0: raise ZeroDivisionError("division by zero") return operator.truediv(a, b) def _safe_floordiv(a, b): + global _total_ops + _total_ops += 1 if b == 0: raise ZeroDivisionError("division by zero") return operator.floordiv(a, b) def _safe_mod(a, b): + global _total_ops + _total_ops += 1 if b == 0: raise ZeroDivisionError("modulo by zero") return operator.mod(a, b) @@ -94,23 +105,32 @@ def _validate_ast(tree): raise ValueError("expression too deep") def _eval(node, ops, funcs, consts, last_result): + global _success_ops, _total_ops if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): return _assert_numeric_limits(node.value) if isinstance(node, ast.UnaryOp) and type(node.op) in ops: + _total_ops += 1 v = _eval(node.operand, ops, funcs, consts, last_result) - return _assert_numeric_limits(ops[type(node.op)](v)) + r = _assert_numeric_limits(ops[type(node.op)](v)) + _success_ops += 1 + return r if isinstance(node, ast.BinOp) and type(node.op) in ops: + _total_ops += 1 l = _eval(node.left, ops, funcs, consts, last_result) r = _eval(node.right, ops, funcs, consts, last_result) - return _assert_numeric_limits(ops[type(node.op)](l, r)) + r = _assert_numeric_limits(ops[type(node.op)](l, r)) + _success_ops += 1 + return r if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.keywords == []: fname = node.func.id if fname in funcs: + _total_ops += 1 args = [_eval(a, ops, funcs, consts, last_result) for a in node.args] if fname == "factorial": if not (len(args) == 1 and isinstance(args[0], int) and 0 <= args[0] <= 100000): raise OverflowError("factorial argument out of allowed range") r = funcs[fname](*args) + _success_ops += 1 return _assert_numeric_limits(r) if isinstance(node, ast.Name): if node.id in consts: @@ -145,6 +165,7 @@ def _friendly_error(e: Exception) -> str: return f"Error: {e}" def main(): + global _total_ops, _success_ops mode = input("Choose calculator mode (simple/scientific): ").lower() if mode == "simple": current_ops = _SIMPLE_OPS; current_funcs = _SIMPLE_FUNCS; current_consts = _SIMPLE_CONSTS @@ -158,9 +179,11 @@ def main(): try: result = calculate(" ".join(sys.argv[1:]), current_ops, current_funcs, current_consts, last_result) print(result) + _success_ops += 1 last_result = result except Exception as e: print(_friendly_error(e)) + print(f"Done {_success_ops} operations out of {_total_ops}.") return while True: try: @@ -172,8 +195,10 @@ def main(): result = calculate(s, current_ops, current_funcs, current_consts, last_result) print(result) last_result = result + print(f"Done {_success_ops} operations out of {_total_ops}.") except Exception as e: print(_friendly_error(e)) + print(f"Done {_success_ops} operations out of {_total_ops}.") if __name__ == "__main__": main() From 1a59fa2cd47384ddb6e4b7cd4fd5cf2db6f4b38c Mon Sep 17 00:00:00 2001 From: LuckyLuke255 <105583948+LuckyLuke255@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:11:11 +0100 Subject: [PATCH 10/11] Update tests_project.py --- feature1/tests_project.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/feature1/tests_project.py b/feature1/tests_project.py index 3d11ee5..35f1633 100644 --- a/feature1/tests_project.py +++ b/feature1/tests_project.py @@ -29,8 +29,6 @@ def _assert_numeric_limits(x): return x def _safe_pow(a, b): - global _total_ops - _total_ops += 1 if (isinstance(a, complex) or isinstance(b, complex)) and not _ALLOW_COMPLEX: raise ValueError("complex results are not supported") if isinstance(b, int): @@ -43,22 +41,16 @@ def _safe_pow(a, b): return _assert_numeric_limits(r) def _safe_div(a, b): - global _total_ops - _total_ops += 1 if b == 0: raise ZeroDivisionError("division by zero") return operator.truediv(a, b) def _safe_floordiv(a, b): - global _total_ops - _total_ops += 1 if b == 0: raise ZeroDivisionError("division by zero") return operator.floordiv(a, b) def _safe_mod(a, b): - global _total_ops - _total_ops += 1 if b == 0: raise ZeroDivisionError("modulo by zero") return operator.mod(a, b) @@ -179,7 +171,6 @@ def main(): try: result = calculate(" ".join(sys.argv[1:]), current_ops, current_funcs, current_consts, last_result) print(result) - _success_ops += 1 last_result = result except Exception as e: print(_friendly_error(e)) From e2ccf532ba6c6c614a9583ab29bd1536a8154520 Mon Sep 17 00:00:00 2001 From: LuckyLuke255 <105583948+LuckyLuke255@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:26:55 +0100 Subject: [PATCH 11/11] Update tests_project.py --- feature1/tests_project.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/feature1/tests_project.py b/feature1/tests_project.py index 35f1633..42e914d 100644 --- a/feature1/tests_project.py +++ b/feature1/tests_project.py @@ -115,8 +115,8 @@ def _eval(node, ops, funcs, consts, last_result): return r if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.keywords == []: fname = node.func.id + _total_ops += 1 if fname in funcs: - _total_ops += 1 args = [_eval(a, ops, funcs, consts, last_result) for a in node.args] if fname == "factorial": if not (len(args) == 1 and isinstance(args[0], int) and 0 <= args[0] <= 100000): @@ -124,20 +124,34 @@ def _eval(node, ops, funcs, consts, last_result): r = funcs[fname](*args) _success_ops += 1 return _assert_numeric_limits(r) + raise ValueError("invalid expression") if isinstance(node, ast.Name): if node.id in consts: return _assert_numeric_limits(consts[node.id]) if node.id == "ans": return _assert_numeric_limits(last_result) + if isinstance(node, (ast.UnaryOp, ast.BinOp, ast.Call)): + _total_ops += 1 raise ValueError("invalid expression") def calculate(expr, ops, funcs, consts, last_result): + global _total_ops if not isinstance(expr, str) or not expr.strip(): + _total_ops += 1 raise ValueError("invalid expression") if len(expr) > _MAX_EXPR_LEN: + _total_ops += 1 raise ValueError("expression too long") - tree = ast.parse(expr, mode="eval") - _validate_ast(tree) + try: + tree = ast.parse(expr, mode="eval") + except SyntaxError: + _total_ops += 1 + raise ValueError("invalid expression") + try: + _validate_ast(tree) + except Exception as e: + _total_ops += 1 + raise e return _eval(tree.body, ops, funcs, consts, last_result) def _friendly_error(e: Exception) -> str: