Skip to content
This repository was archived by the owner on Jan 15, 2026. It is now read-only.

Commit 0f975c3

Browse files
committed
add type annotations and docstrings to devlib
Most of the files are covered, but some of the instruments and unused platforms are not augmented
1 parent 5425f4a commit 0f975c3

55 files changed

Lines changed: 9221 additions & 3989 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ devlib/bin/scripts/shutils
77
doc/_build/
88
build/
99
dist/
10+
.venv/
11+
.vscode/
12+
venv/
13+
.history/

devlib/_target_runner.py

Lines changed: 93 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 ARM Limited
1+
# Copyright 2024-2025 ARM Limited
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -17,15 +17,24 @@
1717
Target runner and related classes are implemented here.
1818
"""
1919

20-
import logging
2120
import os
2221
import time
22+
2323
from platform import machine
24+
from typing import Optional, cast, Protocol, TYPE_CHECKING, Union
25+
from typing_extensions import NotRequired, LiteralString, TypedDict
26+
if TYPE_CHECKING:
27+
from _typeshed import StrPath, BytesPath
28+
from devlib.platform import Platform
29+
else:
30+
StrPath = str
31+
BytesPath = bytes
2432

2533
from devlib.exception import (TargetStableError, HostError)
26-
from devlib.target import LinuxTarget
27-
from devlib.utils.misc import get_subprocess, which
34+
from devlib.target import LinuxTarget, Target
35+
from devlib.utils.misc import get_subprocess, which, get_logger
2836
from devlib.utils.ssh import SshConnection
37+
from devlib.utils.annotation_helpers import SubprocessCommand, SshUserConnectionSettings
2938

3039

3140
class TargetRunner:
@@ -36,16 +45,14 @@ class TargetRunner:
3645
(e.g., :class:`QEMUTargetRunner`).
3746
3847
:param target: Specifies type of target per :class:`Target` based classes.
39-
:type target: Target
4048
"""
4149

4250
def __init__(self,
43-
target):
51+
target: Target) -> None:
4452
self.target = target
53+
self.logger = get_logger(self.__class__.__name__)
4554

46-
self.logger = logging.getLogger(self.__class__.__name__)
47-
48-
def __enter__(self):
55+
def __enter__(self) -> 'TargetRunner':
4956
return self
5057

5158
def __exit__(self, *_):
@@ -58,29 +65,25 @@ class SubprocessTargetRunner(TargetRunner):
5865
5966
:param runner_cmd: The command to start runner process (e.g.,
6067
``qemu-system-aarch64 -kernel Image -append "console=ttyAMA0" ...``).
61-
:type runner_cmd: list(str)
6268
6369
:param target: Specifies type of target per :class:`Target` based classes.
64-
:type target: Target
6570
6671
:param connect: Specifies if :class:`TargetRunner` should try to connect
6772
target after launching it, defaults to True.
68-
:type connect: bool or None
6973
7074
:param boot_timeout: Timeout for target's being ready for SSH access in
7175
seconds, defaults to 60.
72-
:type boot_timeout: int or None
7376
7477
:raises HostError: if it cannot execute runner command successfully.
7578
7679
:raises TargetStableError: if Target is inaccessible.
7780
"""
7881

7982
def __init__(self,
80-
runner_cmd,
81-
target,
82-
connect=True,
83-
boot_timeout=60):
83+
runner_cmd: SubprocessCommand,
84+
target: Target,
85+
connect: bool = True,
86+
boot_timeout: int = 60):
8487
super().__init__(target=target)
8588

8689
self.boot_timeout = boot_timeout
@@ -90,7 +93,7 @@ def __init__(self,
9093
try:
9194
self.runner_process = get_subprocess(runner_cmd)
9295
except Exception as ex:
93-
raise HostError(f'Error while running "{runner_cmd}": {ex}') from ex
96+
raise HostError(f'Error while running "{runner_cmd!r}": {ex}') from ex
9497

9598
if connect:
9699
self.wait_boot_complete()
@@ -107,16 +110,16 @@ def __exit__(self, *_):
107110

108111
self.terminate()
109112

110-
def wait_boot_complete(self):
113+
def wait_boot_complete(self) -> None:
111114
"""
112-
Wait for target OS to finish boot up and become accessible over SSH in at most
113-
``SubprocessTargetRunner.boot_timeout`` seconds.
115+
Wait for the target OS to finish booting and become accessible within
116+
:attr:`boot_timeout` seconds.
114117
115-
:raises TargetStableError: In case of timeout.
118+
:raises TargetStableError: If the target is inaccessible after the timeout.
116119
"""
117120

118121
start_time = time.time()
119-
elapsed = 0
122+
elapsed: float = 0.0
120123
while self.boot_timeout >= elapsed:
121124
try:
122125
self.target.connect(timeout=self.boot_timeout - elapsed)
@@ -132,9 +135,9 @@ def wait_boot_complete(self):
132135
self.terminate()
133136
raise TargetStableError(f'Target is inaccessible for {self.boot_timeout} seconds!')
134137

135-
def terminate(self):
138+
def terminate(self) -> None:
136139
"""
137-
Terminate ``SubprocessTargetRunner.runner_process``.
140+
Terminate the subprocess associated with this runner.
138141
"""
139142

140143
self.logger.debug('Killing target runner...')
@@ -147,10 +150,9 @@ class NOPTargetRunner(TargetRunner):
147150
Class for implementing a target runner which does nothing except providing .target attribute.
148151
149152
:param target: Specifies type of target per :class:`Target` based classes.
150-
:type target: Target
151153
"""
152154

153-
def __init__(self, target):
155+
def __init__(self, target: Target) -> None:
154156
super().__init__(target=target)
155157

156158
def __enter__(self):
@@ -159,11 +161,61 @@ def __enter__(self):
159161
def __exit__(self, *_):
160162
pass
161163

162-
def terminate(self):
164+
def terminate(self) -> None:
163165
"""
164166
Nothing to terminate for NOP target runners.
165167
Defined to be compliant with other runners (e.g., ``SubprocessTargetRunner``).
166168
"""
169+
pass
170+
171+
172+
class QEMUTargetUserSettings(TypedDict, total=False):
173+
kernel_image: str
174+
arch: NotRequired[str]
175+
cpu_type: NotRequired[str]
176+
initrd_image: str
177+
mem_size: NotRequired[int]
178+
num_cores: NotRequired[int]
179+
num_threads: NotRequired[int]
180+
cmdline: NotRequired[str]
181+
enable_kvm: NotRequired[bool]
182+
183+
184+
class QEMUTargetRunnerSettings(TypedDict, total=False):
185+
kernel_image: str
186+
arch: str
187+
cpu_type: str
188+
initrd_image: str
189+
mem_size: int
190+
num_cores: int
191+
num_threads: int
192+
cmdline: str
193+
enable_kvm: bool
194+
195+
196+
class SshConnectionSettings(TypedDict, total=False):
197+
username: str
198+
password: str
199+
keyfile: Optional[Union[LiteralString, StrPath, BytesPath]]
200+
host: str
201+
port: int
202+
timeout: float
203+
platform: 'Platform'
204+
sudo_cmd: str
205+
strict_host_check: bool
206+
use_scp: bool
207+
poll_transfers: bool
208+
start_transfer_poll_delay: int
209+
total_transfer_timeout: int
210+
transfer_poll_period: int
211+
212+
213+
class QEMUTargetRunnerTargetFactory(Protocol):
214+
"""
215+
Protocol for Lambda function for creating :class:`Target` based object.
216+
"""
217+
def __call__(self, *, connect: bool, conn_cls, connection_settings: SshConnectionSettings) -> Target:
218+
...
167219

168220

169221
class QEMUTargetRunner(SubprocessTargetRunner):
@@ -177,7 +229,7 @@ class QEMUTargetRunner(SubprocessTargetRunner):
177229
178230
* ``arch``: Architecture type. Defaults to ``aarch64``.
179231
180-
* ``cpu_types``: List of CPU ids for QEMU. The list only contains ``cortex-a72`` by
232+
* ``cpu_type``: List of CPU ids for QEMU. The list only contains ``cortex-a72`` by
181233
default. This parameter is valid for Arm architectures only.
182234
183235
* ``initrd_image``: This points to the location of initrd image (e.g.,
@@ -197,36 +249,37 @@ class QEMUTargetRunner(SubprocessTargetRunner):
197249
* ``enable_kvm``: Specifies if KVM will be used as accelerator in QEMU or not.
198250
Enabled by default if host architecture matches with target's for improving
199251
QEMU performance.
200-
:type qemu_settings: Dict
201252
202253
:param connection_settings: the dictionary to store connection settings
203254
of ``Target.connection_settings``, defaults to None.
204-
:type connection_settings: Dict or None
205255
206256
:param make_target: Lambda function for creating :class:`Target` based object.
207-
:type make_target: func or None
208257
209258
:Variable positional arguments: Forwarded to :class:`TargetRunner`.
210259
211260
:raises FileNotFoundError: if QEMU executable, kernel or initrd image cannot be found.
212261
"""
213262

214263
def __init__(self,
215-
qemu_settings,
216-
connection_settings=None,
217-
make_target=LinuxTarget,
218-
**args):
264+
qemu_settings: QEMUTargetUserSettings,
265+
connection_settings: Optional[SshUserConnectionSettings] = None,
266+
make_target: QEMUTargetRunnerTargetFactory = cast(QEMUTargetRunnerTargetFactory, LinuxTarget),
267+
**args) -> None:
219268

220-
self.connection_settings = {
269+
default_connection_settings = {
221270
'host': '127.0.0.1',
222271
'port': 8022,
223272
'username': 'root',
224273
'password': 'root',
225274
'strict_host_check': False,
226275
}
227-
self.connection_settings = {**self.connection_settings, **(connection_settings or {})}
228276

229-
qemu_args = {
277+
self.connection_settings: SshConnectionSettings = cast(SshConnectionSettings, {
278+
**default_connection_settings,
279+
**(connection_settings or {})
280+
})
281+
282+
qemu_default_args = {
230283
'arch': 'aarch64',
231284
'cpu_type': 'cortex-a72',
232285
'mem_size': 512,
@@ -235,7 +288,7 @@ def __init__(self,
235288
'cmdline': 'console=ttyAMA0',
236289
'enable_kvm': True,
237290
}
238-
qemu_args = {**qemu_args, **qemu_settings}
291+
qemu_args: QEMUTargetRunnerSettings = cast(QEMUTargetRunnerSettings, {**qemu_default_args, **qemu_settings})
239292

240293
qemu_executable = f'qemu-system-{qemu_args["arch"]}'
241294
qemu_path = which(qemu_executable)

devlib/collector/__init__.py

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2015 ARM Limited
1+
# Copyright 2015-2025 ARM Limited
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -16,27 +16,63 @@
1616
import logging
1717

1818
from devlib.utils.types import caseless_string
19+
from devlib.utils.misc import get_logger
20+
from typing import TYPE_CHECKING, Optional, List
21+
if TYPE_CHECKING:
22+
from devlib.target import Target
23+
1924

2025
class CollectorBase(object):
26+
"""
27+
The `Collector` API provide a consistent way of collecting arbitrary data from
28+
a target. Data is collected via an instance of a class derived from :class:`CollectorBase`.
2129
22-
def __init__(self, target):
30+
:param target: The devlib Target from which data will be collected.
31+
"""
32+
def __init__(self, target: 'Target'):
2333
self.target = target
24-
self.logger = logging.getLogger(self.__class__.__name__)
25-
self.output_path = None
26-
27-
def reset(self):
34+
self.logger: logging.Logger = get_logger(self.__class__.__name__)
35+
self.output_path: Optional[str] = None
36+
37+
def reset(self) -> None:
38+
"""
39+
This can be used to configure a collector for collection. This must be invoked
40+
before :meth:`start()` is called to begin collection.
41+
"""
2842
pass
2943

30-
def start(self):
44+
def start(self) -> None:
45+
"""
46+
Starts collecting from the target.
47+
"""
3148
pass
3249

3350
def stop(self):
51+
"""
52+
Stops collecting from target. Must be called after
53+
:func:`start()`.
54+
"""
3455
pass
3556

36-
def set_output(self, output_path):
57+
def set_output(self, output_path: str) -> None:
58+
"""
59+
Configure the output path for the particular collector. This will be either
60+
a directory or file path which will be used when storing the data. Please see
61+
the individual Collector documentation for more information.
62+
63+
:param output_path: The path (file or directory) to which data will be saved.
64+
"""
3765
self.output_path = output_path
3866

39-
def get_data(self):
67+
def get_data(self) -> 'CollectorOutput':
68+
"""
69+
The collected data will be return via the previously specified output_path.
70+
This method will return a :class:`CollectorOutput` object which is a subclassed
71+
list object containing individual ``CollectorOutputEntry`` objects with details
72+
about the individual output entry.
73+
74+
:raises RuntimeError: If ``output_path`` has not been set.
75+
"""
4076
return CollectorOutput()
4177

4278
def __enter__(self):
@@ -47,18 +83,26 @@ def __enter__(self):
4783
def __exit__(self, exc_type, exc_value, traceback):
4884
self.stop()
4985

86+
5087
class CollectorOutputEntry(object):
88+
"""
89+
This object is designed to allow for the output of a collector to be processed
90+
generically. The object will behave as a regular string containing the path to
91+
underlying output path and can be used directly in ``os.path`` operations.
5192
52-
path_kinds = ['file', 'directory']
93+
:param path: The file path of the collected output data.
94+
:param path_kind: The type of output. Must be one of ``file`` or ``directory``.
95+
"""
96+
path_kinds: List[str] = ['file', 'directory']
5397

54-
def __init__(self, path, path_kind):
55-
self.path = path
98+
def __init__(self, path: str, path_kind: str):
99+
self.path = path # path for the corresponding output item
56100

57101
path_kind = caseless_string(path_kind)
58102
if path_kind not in self.path_kinds:
59103
msg = '{} is not a valid path_kind [{}]'
60104
raise ValueError(msg.format(path_kind, ' '.join(self.path_kinds)))
61-
self.path_kind = path_kind
105+
self.path_kind = path_kind # file or directory
62106

63107
def __str__(self):
64108
return self.path

0 commit comments

Comments
 (0)