Fix lint, format and typing findings and pin dev tools
Order imports, format with ruff, make the account key constants Final so mypy --strict accepts them, and type the test helpers. Configure ruff and mypy in pyproject.toml and pin the dev tool versions. All modules, classes and functions already carry Doxygen comments; doxygen Doxyfile builds without warnings. Task: MIL-004#2 Closes #18
This commit is contained in:
+14
-1
@@ -12,7 +12,7 @@ license = { file = "LICENSE" }
|
|||||||
dependencies = []
|
dependencies = []
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = ["pytest>=8"]
|
dev = ["pytest>=9,<10", "ruff>=0.16,<0.17", "mypy>=2.4,<3"]
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["src"]
|
where = ["src"]
|
||||||
@@ -20,3 +20,16 @@ where = ["src"]
|
|||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
testpaths = ["tests"]
|
testpaths = ["tests"]
|
||||||
pythonpath = ["src"]
|
pythonpath = ["src"]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 88
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "I", "UP", "FLY", "B"]
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
"src/higher_lower/game_data.py" = ["E501"] # assignment data kept verbatim
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
strict = true
|
||||||
|
mypy_path = "src"
|
||||||
|
|||||||
+28
-14
@@ -9,23 +9,37 @@ string, so Doxygen does not read the backticks in the art as markup.
|
|||||||
## The game logo.
|
## The game logo.
|
||||||
logo = (
|
logo = (
|
||||||
"\n"
|
"\n"
|
||||||
r" __ ___ __ " "\n"
|
r" __ ___ __ "
|
||||||
r" / / / (_)___ _/ /_ ___ _____" "\n"
|
"\n"
|
||||||
r" / /_/ / / __ `/ __ \/ _ \/ ___/" "\n"
|
r" / / / (_)___ _/ /_ ___ _____"
|
||||||
r" / __ / / /_/ / / / / __/ / " "\n"
|
"\n"
|
||||||
r"/_/ ///_/\__, /_/ /_/\___/_/ " "\n"
|
r" / /_/ / / __ `/ __ \/ _ \/ ___/"
|
||||||
r" / / /____/_ _____ _____" "\n"
|
"\n"
|
||||||
r" / / / __ \ | /| / / _ \/ ___/" "\n"
|
r" / __ / / /_/ / / / / __/ / "
|
||||||
r" / /___/ /_/ / |/ |/ / __/ / " "\n"
|
"\n"
|
||||||
r"/_____/\____/|__/|__/\___/_/ " "\n"
|
r"/_/ ///_/\__, /_/ /_/\___/_/ "
|
||||||
|
"\n"
|
||||||
|
r" / / /____/_ _____ _____"
|
||||||
|
"\n"
|
||||||
|
r" / / / __ \ | /| / / _ \/ ___/"
|
||||||
|
"\n"
|
||||||
|
r" / /___/ /_/ / |/ |/ / __/ / "
|
||||||
|
"\n"
|
||||||
|
r"/_____/\____/|__/|__/\___/_/ "
|
||||||
|
"\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
## The "vs" shown between the two accounts.
|
## The "vs" shown between the two accounts.
|
||||||
vs = (
|
vs = (
|
||||||
"\n"
|
"\n"
|
||||||
r" _ __ " "\n"
|
r" _ __ "
|
||||||
r"| | / /____" "\n"
|
"\n"
|
||||||
r"| | / / ___/" "\n"
|
r"| | / /____"
|
||||||
r"| |/ (__ ) " "\n"
|
"\n"
|
||||||
r"|___/____(_)" "\n"
|
r"| | / / ___/"
|
||||||
|
"\n"
|
||||||
|
r"| |/ (__ ) "
|
||||||
|
"\n"
|
||||||
|
r"|___/____(_)"
|
||||||
|
"\n"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
@brief Constants shared by the game modules.
|
@brief Constants shared by the game modules.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
## Keys of an account dictionary.
|
## Keys of an account dictionary.
|
||||||
KEY_NAME = "name"
|
KEY_NAME: Final = "name"
|
||||||
KEY_FOLLOWER_COUNT = "follower_count"
|
KEY_FOLLOWER_COUNT: Final = "follower_count"
|
||||||
KEY_DESCRIPTION = "description"
|
KEY_DESCRIPTION: Final = "description"
|
||||||
KEY_COUNTRY = "country"
|
KEY_COUNTRY: Final = "country"
|
||||||
|
|
||||||
## The two choices the player can type, in lower case.
|
## The two choices the player can type, in lower case.
|
||||||
CHOICE_A = "a"
|
CHOICE_A = "a"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""@file game_logic.py
|
"""@file game_logic.py
|
||||||
@brief Pure functions of the game: drawing accounts, describing them and checking guesses.
|
@brief Pure game functions: draw accounts, describe them, check guesses.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import random
|
import random
|
||||||
@@ -9,9 +9,9 @@ from higher_lower.constants import (
|
|||||||
ACCOUNT_TEMPLATE,
|
ACCOUNT_TEMPLATE,
|
||||||
CHOICE_A,
|
CHOICE_A,
|
||||||
CHOICE_B,
|
CHOICE_B,
|
||||||
|
KEY_COUNTRY,
|
||||||
KEY_DESCRIPTION,
|
KEY_DESCRIPTION,
|
||||||
KEY_NAME,
|
KEY_NAME,
|
||||||
KEY_COUNTRY,
|
|
||||||
)
|
)
|
||||||
from higher_lower.game_data import Account, data
|
from higher_lower.game_data import Account, data
|
||||||
|
|
||||||
|
|||||||
+23
-6
@@ -14,20 +14,37 @@ from higher_lower.game import ask_choice, play_game, show_round
|
|||||||
from higher_lower.game_data import Account
|
from higher_lower.game_data import Account
|
||||||
|
|
||||||
ACCOUNTS: list[Account] = [
|
ACCOUNTS: list[Account] = [
|
||||||
{"name": "First", "follower_count": 30, "description": "Test one", "country": "Denmark"},
|
{
|
||||||
{"name": "Second", "follower_count": 20, "description": "Test two", "country": "Norway"},
|
"name": "First",
|
||||||
{"name": "Third", "follower_count": 10, "description": "Test three", "country": "Sweden"},
|
"follower_count": 30,
|
||||||
|
"description": "Test one",
|
||||||
|
"country": "Denmark",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Second",
|
||||||
|
"follower_count": 20,
|
||||||
|
"description": "Test two",
|
||||||
|
"country": "Norway",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Third",
|
||||||
|
"follower_count": 10,
|
||||||
|
"description": "Test three",
|
||||||
|
"country": "Sweden",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class FirstRng(random.Random):
|
class FirstRng(random.Random):
|
||||||
"""@brief Random generator that always takes the first entries, so a game is fixed."""
|
"""@brief Random generator that takes the first entries, so a game is fixed."""
|
||||||
|
|
||||||
def sample(self, population: Sequence, k: int, **kwargs: object) -> list: # type: ignore[override]
|
def sample( # type: ignore[override]
|
||||||
|
self, population: Sequence[Account], k: int, **kwargs: object
|
||||||
|
) -> list[Account]:
|
||||||
"""@brief Return the first k entries."""
|
"""@brief Return the first k entries."""
|
||||||
return list(population)[:k]
|
return list(population)[:k]
|
||||||
|
|
||||||
def choice(self, seq: Sequence) -> Account: # type: ignore[override]
|
def choice(self, seq: Sequence[Account]) -> Account: # type: ignore[override]
|
||||||
"""@brief Return the first entry."""
|
"""@brief Return the first entry."""
|
||||||
return seq[0]
|
return seq[0]
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ def test_format_data_describes_the_account() -> None:
|
|||||||
"country": "United States",
|
"country": "United States",
|
||||||
}
|
}
|
||||||
assert (
|
assert (
|
||||||
format_data(account)
|
format_data(account) == "Nike, a Sportswear multinational, from United States"
|
||||||
== "Nike, a Sportswear multinational, from United States"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user