Skip to content

Commit 5fc521d

Browse files
committed
chore: strict annotations
We annotate the entire codebase to support `--strict` mode in mypy.
1 parent 3bc26d4 commit 5fc521d

File tree

5 files changed

+30
-26
lines changed

5 files changed

+30
-26
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,5 @@ default.db
7575
# Generated reports and test reports
7676
reports/*
7777
test_reports/*
78+
79+
**/.claude/settings.local.json

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ test: clean ## run tests in the current virtualenv
7777
pytest
7878

7979
test-types: ## run mypy tests on the whole codebase
80-
mypy --ignore-missing-imports code_annotations/ tests/ test_utils/ setup.py
80+
mypy --ignore-missing-imports --strict code_annotations/ tests/ test_utils/ setup.py
8181

8282
diff_cover: test ## find diff lines that need test coverage
8383
diff-cover coverage.xml

code_annotations/annotation_errors.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
List possible annotation error types.
33
"""
44
from collections import namedtuple
5+
import typing as t
56

67
AnnotationError = namedtuple(
78
"AnnotationError", ["message", "symbol", "description"]
89
)
910

1011
# The TYPES list should contain all AnnotationError instances. This list can then be parsed by others, for instance
1112
# to expose errors to pylint.
12-
TYPES = []
13+
TYPES: list[AnnotationError] = []
1314

15+
T = t.TypeVar('T', bound=AnnotationError)
1416

15-
def add_error_type(message, symbol, description):
17+
def add_error_type(message: str, symbol: str, description: str) -> AnnotationError:
1618
"""
1719
Create an AnnotationError instance and add it to TYPES.
1820
"""
@@ -32,32 +34,32 @@ def add_error_type(message, symbol, description):
3234
# It is important to preserve the insertion order of these error types in the TYPES list, as edx-lint uses the error
3335
# type indices to generate numerical pylint IDs. If the insertion order is changed, the pylint IDs will change too,
3436
# which might cause incompatibilities down the road. Thus, new items should be added at the end.
35-
InvalidChoice = add_error_type(
37+
InvalidChoice: AnnotationError = add_error_type(
3638
'"%s" is not a valid choice for "%s". Expected one of %s.',
3739
"annotation-invalid-choice",
3840
"Emitted when the value of a choice field is not one of the valid choices",
3941
)
40-
DuplicateChoiceValue = add_error_type(
42+
DuplicateChoiceValue: AnnotationError = add_error_type(
4143
'"%s" is already present in this annotation.',
4244
"annotation-duplicate-choice-value",
4345
"Emitted when duplicate values are found in a choice field",
4446
)
45-
MissingChoiceValue = add_error_type(
47+
MissingChoiceValue: AnnotationError = add_error_type(
4648
'no value found for "%s". Expected one of %s.',
4749
"annotation-missing-choice-value",
4850
"Emitted when a choice field does not have any value",
4951
)
50-
InvalidToken = add_error_type(
52+
InvalidToken: AnnotationError = add_error_type(
5153
"'%s' token does not belong to group '%s'. Expected one of: %s",
5254
"annotation-invalid-token",
5355
"Emitted when a token is found in a group for which it is not valid",
5456
)
55-
DuplicateToken = add_error_type(
57+
DuplicateToken: AnnotationError = add_error_type(
5658
"found duplicate token '%s'",
5759
"annotation-duplicate-token",
5860
"Emitted when a token is found twice in a group",
5961
)
60-
MissingToken = add_error_type(
62+
MissingToken: AnnotationError = add_error_type(
6163
"missing non-optional annotation: '%s'",
6264
"annotation-missing-token",
6365
"Emitted when a required token is missing from a group",

code_annotations/helpers.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
import os
55
import re
66
import sys
7+
import typing as t
78
from io import StringIO
89
from pprint import pprint
910

1011
import click
1112

1213

13-
def fail(msg):
14+
def fail(msg: str) -> t.NoReturn:
1415
"""
1516
Log the message and exit.
1617
@@ -26,9 +27,9 @@ class VerboseEcho:
2627
Helper to handle verbosity-dependent logging.
2728
"""
2829

29-
verbosity = 1
30+
verbosity: int = 1
3031

31-
def __call__(self, output, **kwargs):
32+
def __call__(self, output: str, **kwargs: t.Any) -> None:
3233
"""
3334
Echo the given output regardless of verbosity level.
3435
@@ -40,18 +41,17 @@ def __call__(self, output, **kwargs):
4041
"""
4142
self.echo(output, **kwargs)
4243

43-
def set_verbosity(self, verbosity):
44+
def set_verbosity(self, verbosity: int) -> None:
4445
"""
4546
Override the default verbosity level.
4647
4748
Args:
4849
verbosity: The verbosity level to set to
49-
kwargs: Any additional keyword args to pass to click.echo
5050
"""
5151
self.verbosity = verbosity
5252
self.echo_v(f"Verbosity level set to {verbosity}")
5353

54-
def echo(self, output, verbosity_level=0, **kwargs):
54+
def echo(self, output: str, verbosity_level: int = 0, **kwargs: t.Any) -> None:
5555
"""
5656
Echo the given output, if over the verbosity threshold.
5757
@@ -63,7 +63,7 @@ def echo(self, output, verbosity_level=0, **kwargs):
6363
if verbosity_level <= self.verbosity:
6464
click.secho(output, **kwargs)
6565

66-
def echo_v(self, output, **kwargs):
66+
def echo_v(self, output: str, **kwargs: t.Any) -> None:
6767
"""
6868
Echo the given output if verbosity level is >= 1.
6969
@@ -73,7 +73,7 @@ def echo_v(self, output, **kwargs):
7373
"""
7474
self.echo(output, 1, **kwargs)
7575

76-
def echo_vv(self, output, **kwargs):
76+
def echo_vv(self, output: str, **kwargs: t.Any) -> None:
7777
"""
7878
Echo the given output if verbosity level is >= 2.
7979
@@ -83,7 +83,7 @@ def echo_vv(self, output, **kwargs):
8383
"""
8484
self.echo(output, 2, **kwargs)
8585

86-
def echo_vvv(self, output, **kwargs):
86+
def echo_vvv(self, output: str, **kwargs: t.Any) -> None:
8787
"""
8888
Echo the given output if verbosity level is >= 3.
8989
@@ -93,7 +93,7 @@ def echo_vvv(self, output, **kwargs):
9393
"""
9494
self.echo(output, 3, **kwargs)
9595

96-
def pprint(self, data, indent=4, verbosity_level=0):
96+
def pprint(self, data: t.Any, indent: int = 4, verbosity_level: int = 0) -> None:
9797
"""
9898
Pretty-print some data with the given verbosity level.
9999
"""
@@ -103,7 +103,7 @@ def pprint(self, data, indent=4, verbosity_level=0):
103103
self.echo(formatted.read(), verbosity_level=verbosity_level)
104104

105105

106-
def clean_abs_path(filename_to_clean, parent_path):
106+
def clean_abs_path(filename_to_clean: str, parent_path: str) -> str:
107107
"""
108108
Safely strips the parent path from the given filename, leaving only the relative path.
109109
@@ -121,7 +121,7 @@ def clean_abs_path(filename_to_clean, parent_path):
121121
return os.path.relpath(filename_to_clean, parent_path)
122122

123123

124-
def get_annotation_regex(annotation_regexes):
124+
def get_annotation_regex(annotation_regexes: list[str]) -> t.Pattern[str]:
125125
"""
126126
Return the full regex to search inside comments for configured annotations.
127127
@@ -166,7 +166,7 @@ def get_annotation_regex(annotation_regexes):
166166
return re.compile(annotation_regex, flags=re.VERBOSE)
167167

168168

169-
def clean_annotation(token, data):
169+
def clean_annotation(token: str, data: str) -> tuple[str, str]:
170170
"""
171171
Clean annotation token and data by stripping all trailing/prefix empty spaces.
172172

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from setuptools import setup
1111

1212

13-
def get_version(*file_paths):
13+
def get_version(*file_paths: str) -> str:
1414
"""
1515
Extract the version string from the file at the given relative path fragments.
1616
"""
@@ -22,14 +22,14 @@ def get_version(*file_paths):
2222
raise RuntimeError("Unable to find version string.")
2323

2424

25-
def load_requirements(*requirements_paths):
25+
def load_requirements(*requirements_paths: str) -> list[str]:
2626
"""
2727
Load all requirements from the specified requirements files.
2828
2929
Returns:
3030
list: Requirements file relative path strings
3131
"""
32-
requirements = set()
32+
requirements: set[str] = set()
3333
for path in requirements_paths:
3434
requirements.update(
3535
line.split("#")[0].strip()
@@ -39,7 +39,7 @@ def load_requirements(*requirements_paths):
3939
return list(requirements)
4040

4141

42-
def is_requirement(line):
42+
def is_requirement(line: str) -> bool:
4343
"""
4444
Return True if the requirement line is a package requirement.
4545

0 commit comments

Comments
 (0)