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