Calculator: empty-input handling, cleaner results, new operations

#1
by arshvinw5 - opened
__pycache__/gradio_calculator.cpython-314.pyc ADDED
Binary file (4.26 kB). View file
 
__pycache__/test_calculator.cpython-314-pytest-9.2.0.pyc ADDED
Binary file (4.44 kB). View file
 
gradio_calculator.py CHANGED
@@ -1,41 +1,80 @@
 
 
1
  import gradio as gr
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
  def calculate(num1, num2, operation):
 
 
 
 
 
 
 
5
  try:
6
  num1 = float(num1)
7
- num2 = float(num2)
8
-
9
- if operation == "+":
10
- result = num1 + num2
11
- elif operation == "-":
12
- result = num1 - num2
13
- elif operation == "*":
14
- result = num1 * num2
15
- elif operation == "/":
16
- if num2 == 0:
17
- return "Cannot divide by zero"
18
- result = num1 / num2
19
- else:
20
- return "Invalid operation"
21
-
22
- return result
23
- except ValueError:
24
  return "Please enter valid numbers"
25
 
 
 
 
 
 
 
26
 
27
  with gr.Blocks() as demo:
28
  gr.Markdown("# Simple Calculator")
29
 
30
  with gr.Row():
31
  num1 = gr.Number(label="First number")
32
- operation = gr.Dropdown(["+", "-", "*", "/"], label="Operation", value="+")
33
  num2 = gr.Number(label="Second number")
34
 
35
  calc_button = gr.Button("Calculate")
36
  output = gr.Textbox(label="Result")
37
 
 
 
 
 
 
38
  calc_button.click(fn=calculate, inputs=[num1, num2, operation], outputs=output)
39
 
40
 
41
- demo.launch()
 
 
1
+ import math
2
+
3
  import gradio as gr
4
 
5
+ OPERATIONS = {
6
+ "+": lambda a, b: a + b,
7
+ "-": lambda a, b: a - b,
8
+ "*": lambda a, b: a * b,
9
+ "/": lambda a, b: a / b,
10
+ "^": lambda a, b: a**b,
11
+ "mod": lambda a, b: a % b,
12
+ "√": lambda a, b: math.sqrt(a),
13
+ "% of": lambda a, b: a / 100 * b,
14
+ }
15
+
16
+ # Operations that only use the first number.
17
+ UNARY_OPS = {"√"}
18
+
19
+
20
+ def format_result(value):
21
+ """Show 5 instead of 5.0 and strip floating-point noise like 0.30000000000000004."""
22
+ value = round(value, 10) + 0.0 # + 0.0 turns -0.0 into 0.0
23
+ if value.is_integer() and abs(value) < 1e15:
24
+ return str(int(value))
25
+ return str(value)
26
+
27
 
28
  def calculate(num1, num2, operation):
29
+ if operation not in OPERATIONS:
30
+ return "Invalid operation"
31
+ if num1 is None:
32
+ return "Please enter the first number"
33
+ if num2 is None and operation not in UNARY_OPS:
34
+ return "Please enter both numbers"
35
+
36
  try:
37
  num1 = float(num1)
38
+ num2 = float(num2) if num2 is not None else 0.0
39
+
40
+ if operation == "/" and num2 == 0:
41
+ return "Cannot divide by zero"
42
+ if operation == "mod" and num2 == 0:
43
+ return "Cannot take modulo by zero"
44
+ if operation == "√" and num1 < 0:
45
+ return "Cannot take square root of a negative number"
46
+
47
+ result = OPERATIONS[operation](num1, num2)
48
+ except OverflowError:
49
+ return "Result is too large"
50
+ except (ValueError, TypeError):
 
 
 
 
51
  return "Please enter valid numbers"
52
 
53
+ if isinstance(result, complex):
54
+ return "Result is not a real number"
55
+ if not math.isfinite(result):
56
+ return "Result is too large"
57
+ return format_result(result)
58
+
59
 
60
  with gr.Blocks() as demo:
61
  gr.Markdown("# Simple Calculator")
62
 
63
  with gr.Row():
64
  num1 = gr.Number(label="First number")
65
+ operation = gr.Dropdown(list(OPERATIONS), label="Operation", value="+")
66
  num2 = gr.Number(label="Second number")
67
 
68
  calc_button = gr.Button("Calculate")
69
  output = gr.Textbox(label="Result")
70
 
71
+ operation.change(
72
+ fn=lambda op: gr.update(visible=op not in UNARY_OPS),
73
+ inputs=operation,
74
+ outputs=num2,
75
+ )
76
  calc_button.click(fn=calculate, inputs=[num1, num2, operation], outputs=output)
77
 
78
 
79
+ if __name__ == "__main__":
80
+ demo.launch()
test_calculator.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from gradio_calculator import calculate
4
+
5
+
6
+ @pytest.mark.parametrize(
7
+ "num1, num2, op, expected",
8
+ [
9
+ (2, 3, "+", "5"),
10
+ (0.1, 0.2, "+", "0.3"),
11
+ (5, 8, "-", "-3"),
12
+ (4, 2.5, "*", "10"),
13
+ (7, 2, "/", "3.5"),
14
+ (1, 3, "/", "0.3333333333"),
15
+ (2, 10, "^", "1024"),
16
+ (10, 3, "mod", "1"),
17
+ (16, None, "√", "4"),
18
+ (2, None, "√", "1.4142135624"),
19
+ (15, 240, "% of", "36"),
20
+ (0, -5, "*", "0"),
21
+ ],
22
+ )
23
+ def test_operations(num1, num2, op, expected):
24
+ assert calculate(num1, num2, op) == expected
25
+
26
+
27
+ @pytest.mark.parametrize(
28
+ "num1, num2, op, expected",
29
+ [
30
+ (None, 3, "+", "Please enter the first number"),
31
+ (2, None, "+", "Please enter both numbers"),
32
+ (None, None, "√", "Please enter the first number"),
33
+ (1, 0, "/", "Cannot divide by zero"),
34
+ (1, 0, "mod", "Cannot take modulo by zero"),
35
+ (-4, None, "√", "Cannot take square root of a negative number"),
36
+ (10, 1000, "^", "Result is too large"),
37
+ (1e308, 10, "*", "Result is too large"),
38
+ (-8, 0.5, "^", "Result is not a real number"),
39
+ (1, 2, "?", "Invalid operation"),
40
+ ],
41
+ )
42
+ def test_errors(num1, num2, op, expected):
43
+ assert calculate(num1, num2, op) == expected