Skip to content

Commit 09641f7

Browse files
dlatypovshuahkh
authored andcommitted
kunit: tool: surface and address more typing issues
The authors of this tool were more familiar with a different type-checker, https://github.com/google/pytype. That's open source, but mypy seems more prevalent (and runs faster). And unlike pytype, mypy doesn't try to infer types so it doesn't check unanotated functions. So annotate ~all functions in kunit tool to increase type-checking coverage. Note: per https://www.python.org/dev/peps/pep-0484/, `__init__()` should be annotated as `-> None`. Doing so makes mypy discover a number of new violations. Exclude main() since we reuse `request` for the different types of requests, which mypy isn't happy about. This commit fixes all but one error, where `TestSuite.status` might be None. Signed-off-by: Daniel Latypov <dlatypov@google.com> Reviewed-by: David Gow <davidgow@google.com> Tested-by: Brendan Higgins <brendanhiggins@google.com> Acked-by: Brendan Higgins <brendanhiggins@google.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
1 parent 8db50be commit 09641f7

5 files changed

Lines changed: 54 additions & 52 deletions

File tree

tools/testing/kunit/kunit.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ class KunitStatus(Enum):
4343
BUILD_FAILURE = auto()
4444
TEST_FAILURE = auto()
4545

46-
def get_kernel_root_path():
47-
parts = sys.argv[0] if not __file__ else __file__
48-
parts = os.path.realpath(parts).split('tools/testing/kunit')
46+
def get_kernel_root_path() -> str:
47+
path = sys.argv[0] if not __file__ else __file__
48+
parts = os.path.realpath(path).split('tools/testing/kunit')
4949
if len(parts) != 2:
5050
sys.exit(1)
5151
return parts[0]
@@ -171,7 +171,7 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree,
171171
exec_result.elapsed_time))
172172
return parse_result
173173

174-
def add_common_opts(parser):
174+
def add_common_opts(parser) -> None:
175175
parser.add_argument('--build_dir',
176176
help='As in the make command, it specifies the build '
177177
'directory.',
@@ -183,13 +183,13 @@ def add_common_opts(parser):
183183
help='Run all KUnit tests through allyesconfig',
184184
action='store_true')
185185

186-
def add_build_opts(parser):
186+
def add_build_opts(parser) -> None:
187187
parser.add_argument('--jobs',
188188
help='As in the make command, "Specifies the number of '
189189
'jobs (commands) to run simultaneously."',
190190
type=int, default=8, metavar='jobs')
191191

192-
def add_exec_opts(parser):
192+
def add_exec_opts(parser) -> None:
193193
parser.add_argument('--timeout',
194194
help='maximum number of seconds to allow for all tests '
195195
'to run. This does not include time taken to build the '
@@ -198,7 +198,7 @@ def add_exec_opts(parser):
198198
default=300,
199199
metavar='timeout')
200200

201-
def add_parse_opts(parser):
201+
def add_parse_opts(parser) -> None:
202202
parser.add_argument('--raw_output', help='don\'t format output from kernel',
203203
action='store_true')
204204
parser.add_argument('--json',

tools/testing/kunit/kunit_config.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import collections
1010
import re
11+
from typing import List, Set
1112

1213
CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$'
1314
CONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$'
@@ -30,10 +31,10 @@ class KconfigParseError(Exception):
3031
class Kconfig(object):
3132
"""Represents defconfig or .config specified using the Kconfig language."""
3233

33-
def __init__(self):
34-
self._entries = []
34+
def __init__(self) -> None:
35+
self._entries = [] # type: List[KconfigEntry]
3536

36-
def entries(self):
37+
def entries(self) -> Set[KconfigEntry]:
3738
return set(self._entries)
3839

3940
def add_entry(self, entry: KconfigEntry) -> None:

tools/testing/kunit/kunit_json.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from kunit_parser import TestStatus
1515

16-
def get_json_result(test_result, def_config, build_dir, json_path):
16+
def get_json_result(test_result, def_config, build_dir, json_path) -> str:
1717
sub_groups = []
1818

1919
# Each test suite is mapped to a KernelCI sub_group

tools/testing/kunit/kunit_kernel.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import os
1212
import shutil
1313
import signal
14+
from typing import Iterator
1415

1516
from contextlib import ExitStack
1617

@@ -39,15 +40,15 @@ class BuildError(Exception):
3940
class LinuxSourceTreeOperations(object):
4041
"""An abstraction over command line operations performed on a source tree."""
4142

42-
def make_mrproper(self):
43+
def make_mrproper(self) -> None:
4344
try:
4445
subprocess.check_output(['make', 'mrproper'], stderr=subprocess.STDOUT)
4546
except OSError as e:
4647
raise ConfigError('Could not call make command: ' + str(e))
4748
except subprocess.CalledProcessError as e:
4849
raise ConfigError(e.output.decode())
4950

50-
def make_olddefconfig(self, build_dir, make_options):
51+
def make_olddefconfig(self, build_dir, make_options) -> None:
5152
command = ['make', 'ARCH=um', 'olddefconfig']
5253
if make_options:
5354
command.extend(make_options)
@@ -60,7 +61,7 @@ def make_olddefconfig(self, build_dir, make_options):
6061
except subprocess.CalledProcessError as e:
6162
raise ConfigError(e.output.decode())
6263

63-
def make_allyesconfig(self, build_dir, make_options):
64+
def make_allyesconfig(self, build_dir, make_options) -> None:
6465
kunit_parser.print_with_timestamp(
6566
'Enabling all CONFIGs for UML...')
6667
command = ['make', 'ARCH=um', 'allyesconfig']
@@ -82,7 +83,7 @@ def make_allyesconfig(self, build_dir, make_options):
8283
kunit_parser.print_with_timestamp(
8384
'Starting Kernel with all configs takes a few minutes...')
8485

85-
def make(self, jobs, build_dir, make_options):
86+
def make(self, jobs, build_dir, make_options) -> None:
8687
command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
8788
if make_options:
8889
command.extend(make_options)
@@ -100,7 +101,7 @@ def make(self, jobs, build_dir, make_options):
100101
if stderr: # likely only due to build warnings
101102
print(stderr.decode())
102103

103-
def linux_bin(self, params, timeout, build_dir):
104+
def linux_bin(self, params, timeout, build_dir) -> None:
104105
"""Runs the Linux UML binary. Must be named 'linux'."""
105106
linux_bin = get_file_path(build_dir, 'linux')
106107
outfile = get_outfile_path(build_dir)
@@ -110,41 +111,41 @@ def linux_bin(self, params, timeout, build_dir):
110111
stderr=subprocess.STDOUT)
111112
process.wait(timeout)
112113

113-
def get_kconfig_path(build_dir):
114+
def get_kconfig_path(build_dir) -> str:
114115
return get_file_path(build_dir, KCONFIG_PATH)
115116

116-
def get_kunitconfig_path(build_dir):
117+
def get_kunitconfig_path(build_dir) -> str:
117118
return get_file_path(build_dir, KUNITCONFIG_PATH)
118119

119-
def get_outfile_path(build_dir):
120+
def get_outfile_path(build_dir) -> str:
120121
return get_file_path(build_dir, OUTFILE_PATH)
121122

122123
class LinuxSourceTree(object):
123124
"""Represents a Linux kernel source tree with KUnit tests."""
124125

125-
def __init__(self):
126+
def __init__(self) -> None:
126127
self._ops = LinuxSourceTreeOperations()
127128
signal.signal(signal.SIGINT, self.signal_handler)
128129

129-
def clean(self):
130+
def clean(self) -> bool:
130131
try:
131132
self._ops.make_mrproper()
132133
except ConfigError as e:
133134
logging.error(e)
134135
return False
135136
return True
136137

137-
def create_kunitconfig(self, build_dir, defconfig=DEFAULT_KUNITCONFIG_PATH):
138+
def create_kunitconfig(self, build_dir, defconfig=DEFAULT_KUNITCONFIG_PATH) -> None:
138139
kunitconfig_path = get_kunitconfig_path(build_dir)
139140
if not os.path.exists(kunitconfig_path):
140141
shutil.copyfile(defconfig, kunitconfig_path)
141142

142-
def read_kunitconfig(self, build_dir):
143+
def read_kunitconfig(self, build_dir) -> None:
143144
kunitconfig_path = get_kunitconfig_path(build_dir)
144145
self._kconfig = kunit_config.Kconfig()
145146
self._kconfig.read_from_file(kunitconfig_path)
146147

147-
def validate_config(self, build_dir):
148+
def validate_config(self, build_dir) -> bool:
148149
kconfig_path = get_kconfig_path(build_dir)
149150
validated_kconfig = kunit_config.Kconfig()
150151
validated_kconfig.read_from_file(kconfig_path)
@@ -158,7 +159,7 @@ def validate_config(self, build_dir):
158159
return False
159160
return True
160161

161-
def build_config(self, build_dir, make_options):
162+
def build_config(self, build_dir, make_options) -> bool:
162163
kconfig_path = get_kconfig_path(build_dir)
163164
if build_dir and not os.path.exists(build_dir):
164165
os.mkdir(build_dir)
@@ -170,7 +171,7 @@ def build_config(self, build_dir, make_options):
170171
return False
171172
return self.validate_config(build_dir)
172173

173-
def build_reconfig(self, build_dir, make_options):
174+
def build_reconfig(self, build_dir, make_options) -> bool:
174175
"""Creates a new .config if it is not a subset of the .kunitconfig."""
175176
kconfig_path = get_kconfig_path(build_dir)
176177
if os.path.exists(kconfig_path):
@@ -186,7 +187,7 @@ def build_reconfig(self, build_dir, make_options):
186187
print('Generating .config ...')
187188
return self.build_config(build_dir, make_options)
188189

189-
def build_um_kernel(self, alltests, jobs, build_dir, make_options):
190+
def build_um_kernel(self, alltests, jobs, build_dir, make_options) -> bool:
190191
try:
191192
if alltests:
192193
self._ops.make_allyesconfig(build_dir, make_options)
@@ -197,7 +198,7 @@ def build_um_kernel(self, alltests, jobs, build_dir, make_options):
197198
return False
198199
return self.validate_config(build_dir)
199200

200-
def run_kernel(self, args=[], build_dir='', timeout=None):
201+
def run_kernel(self, args=[], build_dir='', timeout=None) -> Iterator[str]:
201202
args.extend(['mem=1G', 'console=tty'])
202203
self._ops.linux_bin(args, timeout, build_dir)
203204
outfile = get_outfile_path(build_dir)
@@ -206,6 +207,6 @@ def run_kernel(self, args=[], build_dir='', timeout=None):
206207
for line in file:
207208
yield line
208209

209-
def signal_handler(self, sig, frame):
210+
def signal_handler(self, sig, frame) -> None:
210211
logging.error('Build interruption occurred. Cleaning console.')
211212
subprocess.call(['stty', 'sane'])

tools/testing/kunit/kunit_parser.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,32 @@
1212
from datetime import datetime
1313
from enum import Enum, auto
1414
from functools import reduce
15-
from typing import List, Optional, Tuple
15+
from typing import Iterator, List, Optional, Tuple
1616

1717
TestResult = namedtuple('TestResult', ['status','suites','log'])
1818

1919
class TestSuite(object):
20-
def __init__(self):
21-
self.status = None
22-
self.name = None
23-
self.cases = []
20+
def __init__(self) -> None:
21+
self.status = None # type: Optional[TestStatus]
22+
self.name = ''
23+
self.cases = [] # type: List[TestCase]
2424

25-
def __str__(self):
26-
return 'TestSuite(' + self.status + ',' + self.name + ',' + str(self.cases) + ')'
25+
def __str__(self) -> str:
26+
return 'TestSuite(' + str(self.status) + ',' + self.name + ',' + str(self.cases) + ')'
2727

28-
def __repr__(self):
28+
def __repr__(self) -> str:
2929
return str(self)
3030

3131
class TestCase(object):
32-
def __init__(self):
33-
self.status = None
32+
def __init__(self) -> None:
33+
self.status = None # type: Optional[TestStatus]
3434
self.name = ''
35-
self.log = []
35+
self.log = [] # type: List[str]
3636

37-
def __str__(self):
38-
return 'TestCase(' + self.status + ',' + self.name + ',' + str(self.log) + ')'
37+
def __str__(self) -> str:
38+
return 'TestCase(' + str(self.status) + ',' + self.name + ',' + str(self.log) + ')'
3939

40-
def __repr__(self):
40+
def __repr__(self) -> str:
4141
return str(self)
4242

4343
class TestStatus(Enum):
@@ -51,7 +51,7 @@ class TestStatus(Enum):
5151
kunit_end_re = re.compile('(List of all partitions:|'
5252
'Kernel panic - not syncing: VFS:)')
5353

54-
def isolate_kunit_output(kernel_output):
54+
def isolate_kunit_output(kernel_output) -> Iterator[str]:
5555
started = False
5656
for line in kernel_output:
5757
line = line.rstrip() # line always has a trailing \n
@@ -64,34 +64,34 @@ def isolate_kunit_output(kernel_output):
6464
elif started:
6565
yield line[prefix_len:] if prefix_len > 0 else line
6666

67-
def raw_output(kernel_output):
67+
def raw_output(kernel_output) -> None:
6868
for line in kernel_output:
6969
print(line.rstrip())
7070

7171
DIVIDER = '=' * 60
7272

7373
RESET = '\033[0;0m'
7474

75-
def red(text):
75+
def red(text) -> str:
7676
return '\033[1;31m' + text + RESET
7777

78-
def yellow(text):
78+
def yellow(text) -> str:
7979
return '\033[1;33m' + text + RESET
8080

81-
def green(text):
81+
def green(text) -> str:
8282
return '\033[1;32m' + text + RESET
8383

84-
def print_with_timestamp(message):
84+
def print_with_timestamp(message) -> None:
8585
print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message))
8686

87-
def format_suite_divider(message):
87+
def format_suite_divider(message) -> str:
8888
return '======== ' + message + ' ========'
8989

90-
def print_suite_divider(message):
90+
def print_suite_divider(message) -> None:
9191
print_with_timestamp(DIVIDER)
9292
print_with_timestamp(format_suite_divider(message))
9393

94-
def print_log(log):
94+
def print_log(log) -> None:
9595
for m in log:
9696
print_with_timestamp(m)
9797

0 commit comments

Comments
 (0)