Source code for module_that_can_be_invoked_from_cli
1"""Calculate arithmetic expressions from command line."""
2import argparse
3import enum
4import sys
5import typing
6
7import pydantic
8
9from package_name_to_import_with import (
10 BinaryArithmeticOperator,
11 calculate_results,
12 solve_simplification,
13)
14
15
[docs]
16@enum.unique
17class CalculatorType(str, enum.Enum):
18 """Define supported calculator types."""
19
20 BINARY = "binary"
21 GENERAL = "general"
22
23
[docs]
24class BinaryInputs(pydantic.BaseModel):
25 """Define arguments for binary calculator.
26
27 Attributes
28 ----------
29 calculator_type : typing.Literal[CalculatorType.BINARY]
30 kind of calculator
31 first_number : float
32 first number for the calculation
33 operator : BinaryArithmeticOperator
34 arithmetic operator to be used
35 second_number : float
36 second number for the calculation
37 """
38
39 calculator_type: typing.Literal[CalculatorType.BINARY]
40 first_number: float
41 operator: BinaryArithmeticOperator
42 second_number: float
43
44
[docs]
45class GeneralInputs(pydantic.BaseModel):
46 """Define arguments of general calculator.
47
48 Attributes
49 ----------
50 calculator_type : typing.Literal[CalculatorType.GENERAL]
51 kind of calculator
52 expression : str
53 mathematical expression to be evaluated
54 """
55
56 calculator_type: typing.Literal[CalculatorType.GENERAL]
57 expression: str
58
59
[docs]
60class UserInputs(pydantic.BaseModel):
61 """Define sub-commands and arguments of CLI calculator.
62
63 Attributes
64 ----------
65 inputs : BinaryInputs | GeneralInputs
66 inputs for the calculator
67 """
68
69 inputs: BinaryInputs | GeneralInputs = pydantic.Field(discriminator="calculator_type")
70
71
[docs]
72@pydantic.validate_call(validate_return=True)
73def capture_user_inputs() -> UserInputs:
74 """Capture user inputs for arithmetic expression.
75
76 Returns
77 -------
78 UserInputs
79 captured user inputs
80 """
81 parser = argparse.ArgumentParser(description="calculator for console", add_help=True)
82
83 sub_parsers = parser.add_subparsers(
84 dest="calculator_type", help="types of arithmetic expressions"
85 )
86
87 binary_parser = sub_parsers.add_parser(
88 CalculatorType.BINARY.value, help="basic binary operations"
89 )
90 general_parser = sub_parsers.add_parser(
91 CalculatorType.GENERAL.value, help="standard simplification problems"
92 )
93
94 binary_parser.add_argument("first_number", type=float, help="first number")
95 binary_parser.add_argument(
96 "operator", type=BinaryArithmeticOperator, help="arithmetic operator"
97 )
98 binary_parser.add_argument("second_number", type=float, help="second number")
99
100 general_parser.add_argument("expression", type=str, help="infix expression")
101
102 parsed_arguments, _ = parser.parse_known_args()
103
104 return UserInputs.model_validate({"inputs": vars(parsed_arguments)})
105
106
[docs]
107@pydantic.validate_call(validate_return=True)
108def console_calculator() -> None:
109 """Calculate arithmetic expressions."""
110 user_inputs = capture_user_inputs()
111
112 try:
113 match user_inputs.inputs.calculator_type:
114 case CalculatorType.BINARY:
115 operation_result = calculate_results(
116 user_inputs.inputs.first_number, # type: ignore[union-attr]
117 user_inputs.inputs.operator, # type: ignore[union-attr]
118 user_inputs.inputs.second_number, # type: ignore[union-attr]
119 )
120 case CalculatorType.GENERAL:
121 operation_result = solve_simplification(
122 user_inputs.inputs.expression # type: ignore[union-attr]
123 )
124 case _: # pragma: no cover
125 operation_result = None
126 except Exception as error: # noqa: BLE001 # pylint: disable=broad-except
127 sys.stderr.write(f"Error: {error}")
128 else:
129 sys.stdout.write(f"Result = {operation_result}")
130
131
132if __name__ == "__main__":
133 console_calculator()