Compare commits
5
Commits
planning
...
9030510773
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9030510773 | ||
|
|
5e1067b4f3 | ||
|
|
c90e5afb7c | ||
|
|
1237f41840 | ||
|
|
6809db7930 |
@@ -173,3 +173,6 @@ cython_debug/
|
|||||||
|
|
||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
# Doxygen output
|
||||||
|
docs/doxygen/
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Doxygen configuration for the console calculator.
|
||||||
|
# Generate the API documentation with: doxygen Doxyfile
|
||||||
|
PROJECT_NAME = "Calculator"
|
||||||
|
PROJECT_BRIEF = "Console calculator (Udemy 100 Days of Code, day 10)"
|
||||||
|
OUTPUT_DIRECTORY = docs/doxygen
|
||||||
|
INPUT = src
|
||||||
|
FILE_PATTERNS = *.py
|
||||||
|
RECURSIVE = YES
|
||||||
|
OPTIMIZE_OUTPUT_JAVA = YES
|
||||||
|
EXTRACT_ALL = YES
|
||||||
|
EXTRACT_PRIVATE = NO
|
||||||
|
GENERATE_HTML = YES
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
QUIET = YES
|
||||||
|
WARN_IF_UNDOCUMENTED = YES
|
||||||
|
WARN_NO_PARAMDOC = YES
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=69"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "calc"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A console calculator (Udemy 100 Days of Code, day 10)."
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
license = { text = "AGPL-3.0-or-later" }
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = ["pytest>=8"]
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["src"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = ["tests"]
|
||||||
|
pythonpath = ["src"]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
"""!
|
||||||
|
@file __init__.py
|
||||||
|
@brief Console calculator package (Udemy 100 Days of Code, day 10).
|
||||||
|
"""
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
"""!
|
||||||
|
@file constants.py
|
||||||
|
@brief Constants used by the console calculator.
|
||||||
|
"""
|
||||||
|
|
||||||
|
## Operation symbols, in the order they are offered to the user.
|
||||||
|
SYMBOL_ADD: str = "+"
|
||||||
|
SYMBOL_SUBTRACT: str = "-"
|
||||||
|
SYMBOL_MULTIPLY: str = "*"
|
||||||
|
SYMBOL_DIVIDE: str = "/"
|
||||||
|
|
||||||
|
## Answers accepted after a calculation.
|
||||||
|
ANSWER_CONTINUE: str = "y"
|
||||||
|
ANSWER_NEW: str = "n"
|
||||||
|
ANSWER_QUIT: str = "q"
|
||||||
|
|
||||||
|
## Prompts and messages shown to the user.
|
||||||
|
PROMPT_FIRST_NUMBER: str = "What's the first number?: "
|
||||||
|
PROMPT_NEXT_NUMBER: str = "What's the next number?: "
|
||||||
|
PROMPT_OPERATION: str = "Pick an operation: "
|
||||||
|
PROMPT_AGAIN: str = (
|
||||||
|
f"Type '{ANSWER_CONTINUE}' to continue calculating with {{result}}, "
|
||||||
|
f"'{ANSWER_NEW}' to start a new calculation, "
|
||||||
|
f"or '{ANSWER_QUIT}' to quit: "
|
||||||
|
)
|
||||||
|
MESSAGE_INVALID_NUMBER: str = "That is not a number, please try again."
|
||||||
|
MESSAGE_INVALID_OPERATION: str = "That is not a valid operation, please try again."
|
||||||
|
MESSAGE_INVALID_ANSWER: str = "Please answer with one of the listed letters."
|
||||||
|
MESSAGE_DIVIDE_BY_ZERO: str = "You cannot divide by zero."
|
||||||
|
MESSAGE_GOODBYE: str = "Goodbye!"
|
||||||
|
|
||||||
|
## ASCII title shown when the program starts.
|
||||||
|
ASCII_TITLE: str = r"""
|
||||||
|
____ _ _ _
|
||||||
|
/ ___|__ _ | | ___ _ _| | __ _| |_ ___ _ __
|
||||||
|
| | / _` | | |/ __| | | | |/ _` | __/ _ \| '__|
|
||||||
|
| |__| (_| | | | (__| |_| | | (_| | || (_) | |
|
||||||
|
\____\__,_| |_|\___|\__,_|_|\__,_|\__\___/|_|
|
||||||
|
"""
|
||||||
|
|
||||||
|
## ASCII calculator shown under the title.
|
||||||
|
ASCII_CALCULATOR: str = r"""
|
||||||
|
_____________________
|
||||||
|
| _________________ |
|
||||||
|
| | 0 | |
|
||||||
|
| |_________________| |
|
||||||
|
| ___ ___ ___ ___ |
|
||||||
|
| | 7 | 8 | 9 | | + | |
|
||||||
|
| |___|___|___| |___| |
|
||||||
|
| | 4 | 5 | 6 | | - | |
|
||||||
|
| |___|___|___| |___| |
|
||||||
|
| | 1 | 2 | 3 | | x | |
|
||||||
|
| |___|___|___| |___| |
|
||||||
|
| | . | 0 | = | | / | |
|
||||||
|
| |___|___|___| |___| |
|
||||||
|
|_____________________|
|
||||||
|
"""
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
"""!
|
||||||
|
@file operations.py
|
||||||
|
@brief Arithmetic operations and the dictionary that maps symbols to them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
from calc.constants import (
|
||||||
|
SYMBOL_ADD,
|
||||||
|
SYMBOL_DIVIDE,
|
||||||
|
SYMBOL_MULTIPLY,
|
||||||
|
SYMBOL_SUBTRACT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def add(n1: float, n2: float) -> float:
|
||||||
|
"""!
|
||||||
|
@brief Add two numbers.
|
||||||
|
@param n1 The first number.
|
||||||
|
@param n2 The second number.
|
||||||
|
@return The sum of @p n1 and @p n2.
|
||||||
|
"""
|
||||||
|
return n1 + n2
|
||||||
|
|
||||||
|
|
||||||
|
def subtract(n1: float, n2: float) -> float:
|
||||||
|
"""!
|
||||||
|
@brief Subtract the second number from the first.
|
||||||
|
@param n1 The first number.
|
||||||
|
@param n2 The second number.
|
||||||
|
@return The difference @p n1 minus @p n2.
|
||||||
|
"""
|
||||||
|
return n1 - n2
|
||||||
|
|
||||||
|
|
||||||
|
def multiply(n1: float, n2: float) -> float:
|
||||||
|
"""!
|
||||||
|
@brief Multiply two numbers.
|
||||||
|
@param n1 The first number.
|
||||||
|
@param n2 The second number.
|
||||||
|
@return The product of @p n1 and @p n2.
|
||||||
|
"""
|
||||||
|
return n1 * n2
|
||||||
|
|
||||||
|
|
||||||
|
def divide(n1: float, n2: float) -> float:
|
||||||
|
"""!
|
||||||
|
@brief Divide the first number by the second.
|
||||||
|
@param n1 The dividend.
|
||||||
|
@param n2 The divisor.
|
||||||
|
@return The quotient @p n1 divided by @p n2.
|
||||||
|
@throws ZeroDivisionError When @p n2 is zero.
|
||||||
|
"""
|
||||||
|
return n1 / n2
|
||||||
|
|
||||||
|
|
||||||
|
## Maps an operation symbol to its function. The functions are stored, not called.
|
||||||
|
OPERATIONS: dict[str, Callable[[float, float], float]] = {
|
||||||
|
SYMBOL_ADD: add,
|
||||||
|
SYMBOL_SUBTRACT: subtract,
|
||||||
|
SYMBOL_MULTIPLY: multiply,
|
||||||
|
SYMBOL_DIVIDE: divide,
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
"""!
|
||||||
|
@file test_operations.py
|
||||||
|
@brief Tests for the arithmetic operations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from calc.operations import OPERATIONS, add, divide, multiply, subtract
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_returns_sum() -> None:
|
||||||
|
assert add(2, 3) == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_subtract_returns_difference() -> None:
|
||||||
|
assert subtract(2, 3) == -1
|
||||||
|
|
||||||
|
|
||||||
|
def test_multiply_returns_product() -> None:
|
||||||
|
assert multiply(4, 2.5) == 10
|
||||||
|
|
||||||
|
|
||||||
|
def test_divide_returns_quotient() -> None:
|
||||||
|
assert divide(9, 3) == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_divide_when_divisor_is_zero() -> None:
|
||||||
|
with pytest.raises(ZeroDivisionError):
|
||||||
|
divide(1, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_operations_maps_symbols_to_functions() -> None:
|
||||||
|
assert OPERATIONS == {"+": add, "-": subtract, "*": multiply, "/": divide}
|
||||||
Reference in New Issue
Block a user