Skip to content

Commit 4bc8ad7

Browse files
Passing pyright!
1 parent 75f127b commit 4bc8ad7

Some content is hidden

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

43 files changed

+192
-621
lines changed

tools/pyproject.toml

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ dependencies = [
2020
"jinja2>=2.11.3",
2121
"python-dotenv",
2222
"Click>=8.0", # Need at least this version for pathlib.Path support
23-
"GitPython",
2423
"tqdm",
2524
"tabulate",
2625
"requests>=2.20",
@@ -34,7 +33,7 @@ dependencies = [
3433
# Needed for downloading CMSIS MCU descriptions
3534
"cmsis-pack-manager>=0.5.0",
3635

37-
# USB device detection on Mac
36+
# USB device detection on Windows
3837
"pywin32; platform_system=='Windows'",
3938

4039
# USB device detection on Linux
@@ -74,7 +73,14 @@ unit-tests = [
7473
linters = [
7574
"ruff",
7675
"basedpyright",
77-
"SCons" # For packages imported by the PlatformIO scripts
76+
77+
# To pass Pyright we need all the platform-dependent packages
78+
"platformio",
79+
"SCons",
80+
"psutil",
81+
"pyudev",
82+
"beautifulsoup4",
83+
"lxml"
7884
]
7985
greentea = [
8086
## Additional requirements to install into the Mbed environment when running Greentea tests
@@ -191,6 +197,7 @@ ignore = [
191197
'BLE001', # Allow catching Exception
192198
'S603', # Allow running subprocesses with variable arguments (why is this a lint in the first place??)
193199
'ERA001', # Allow commented code
200+
'T201', # Allow print()
194201

195202
# For now allow old-style type annotations. Currently there's lots of code that uses them, and I am
196203
# not sure if there is a way to upgrade them automatically. And since this project still supports
@@ -233,8 +240,16 @@ reportUnknownArgumentType = false
233240
reportUnknownParameterType = false
234241
reportAny = false
235242
reportExplicitAny = false
243+
reportMissingTypeStubs = false
236244

237245
# Use "medium strict" member variable annotation rules, where the type checker
238246
# is allowed to infer the types of class variables based on what gets assigned in __init__
239247
reportIncompatibleUnannotatedOverride = true
240-
reportUnannotatedClassAttribute = false
248+
reportUnannotatedClassAttribute = false
249+
250+
# Conflicts with Ruff rules
251+
reportImplicitStringConcatenation = false
252+
253+
# Allow isinstance() even when it seems unneccessary based on type annotations.
254+
# This call is useful to check that the runtime types match the annotations.
255+
reportUnnecessaryIsInstance = false

tools/python/mbed_platformio/build_mbed_ce.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
import json
1414
import pathlib
1515
import sys
16+
import typing
1617
from pathlib import Path
1718
from typing import TYPE_CHECKING, Any
1819

19-
import click
20+
from click.parser import split_arg_string
2021
from platformio.proc import exec_command
2122
from SCons.Script import ARGUMENTS, DefaultEnvironment
2223

@@ -29,9 +30,9 @@
2930

3031
# Directories
3132
FRAMEWORK_DIR = Path(platform.get_package_dir("framework-mbed-ce"))
32-
BUILD_DIR = Path(env.subst("$BUILD_DIR"))
33-
PROJECT_DIR = Path(env.subst("$PROJECT_DIR"))
34-
PROJECT_SRC_DIR = Path(env.subst("$PROJECT_SRC_DIR"))
33+
BUILD_DIR = Path(typing.cast(str, env.subst("$BUILD_DIR")))
34+
PROJECT_DIR = Path(typing.cast(str, env.subst("$PROJECT_DIR")))
35+
PROJECT_SRC_DIR = Path(typing.cast(str, env.subst("$PROJECT_SRC_DIR")))
3536
CMAKE_API_DIR = BUILD_DIR / ".cmake" / "api" / "v1"
3637
CMAKE_API_QUERY_DIR = CMAKE_API_DIR / "query"
3738
CMAKE_API_REPLY_DIR = CMAKE_API_DIR / "reply"
@@ -61,7 +62,7 @@
6162

6263

6364
def get_mbed_target() -> str:
64-
board_type = env.subst("$BOARD")
65+
board_type = typing.cast(str, env.subst("$BOARD"))
6566
variant = PIO_VARIANT_TO_MBED_TARGET[board_type] if board_type in PIO_VARIANT_TO_MBED_TARGET else board_type.upper()
6667
return board.get("build.mbed_variant", variant)
6768

@@ -72,7 +73,7 @@ def is_proper_mbed_ce_project() -> bool:
7273

7374
def create_default_project_files() -> None:
7475
if not PROJECT_MBED_APP_JSON5.exists():
75-
PROJECT_MBED_APP_JSON5.write_text(
76+
_ = PROJECT_MBED_APP_JSON5.write_text(
7677
"""
7778
{
7879
"target_overrides": {
@@ -111,9 +112,12 @@ def is_cmake_reconfigure_required() -> bool:
111112

112113
def run_tool(command_and_args: list[str] | None = None) -> None:
113114
result = exec_command(command_and_args)
114-
if result["returncode"] != 0:
115-
sys.stderr.write(result["out"] + "\n")
116-
sys.stderr.write(result["err"] + "\n")
115+
116+
# Note: Pyright seems to think that this will always fail due to missing type annotations
117+
# for exec_command(), but I believe the actual code is fine.
118+
if result["returncode"] != 0: # pyright: ignore[reportUnnecessaryComparison]
119+
print(result["out"], file=sys.stderr)
120+
print(result["err"], file=sys.stderr)
117121
env.Exit(1)
118122

119123
if int(ARGUMENTS.get("PIOVERBOSE", 0)):
@@ -139,7 +143,7 @@ def get_cmake_code_model(cmake_args: list) -> dict:
139143
(BUILD_DIR / "CMakeCache.txt").touch()
140144

141145
if not CMAKE_API_REPLY_DIR.is_dir() or not any(CMAKE_API_REPLY_DIR.iterdir()):
142-
sys.stderr.write("Error: Couldn't find CMake API response file\n")
146+
print("Error: Couldn't find CMake API response file", file=sys.stderr)
143147
env.Exit(1)
144148

145149
codemodel = {}
@@ -153,10 +157,10 @@ def get_cmake_code_model(cmake_args: list) -> dict:
153157

154158

155159
def get_target_config(project_configs: dict, target_index: int) -> dict[str, Any]:
156-
target_json = project_configs.get("targets")[target_index].get("jsonFile", "")
160+
target_json = project_configs["targets"][target_index].get("jsonFile", "")
157161
target_config_file = CMAKE_API_REPLY_DIR / target_json
158162
if not target_config_file.is_file():
159-
sys.stderr.write(f"Error: Couldn't find target config {target_json}\n")
163+
print(f"Error: Couldn't find target config {target_json}", file=sys.stderr)
160164
env.Exit(1)
161165

162166
with target_config_file.open(encoding="utf-8") as fp:
@@ -165,7 +169,7 @@ def get_target_config(project_configs: dict, target_index: int) -> dict[str, Any
165169

166170
def load_target_configurations(cmake_codemodel: dict) -> dict:
167171
configs = {}
168-
project_configs = cmake_codemodel.get("configurations")[0]
172+
project_configs = cmake_codemodel["configurations"][0]
169173
for config in project_configs.get("projects", []):
170174
for target_index in config.get("targetIndexes", []):
171175
target_config = get_target_config(project_configs, target_index)
@@ -242,12 +246,12 @@ def get_app_defines(app_config: dict) -> list[tuple[str, str]]:
242246
"-DMBED_TARGET=" + get_mbed_target(),
243247
"-DUPLOAD_METHOD=NONE", # Disable Mbed CE upload method system as PlatformIO has its own
244248
# Add in any extra options from higher layers
245-
*click.parser.split_arg_string(board.get("build.cmake_extra_args", "")),
249+
*split_arg_string(board.get("build.cmake_extra_args", "")),
246250
]
247251
)
248252

249253
if not project_codemodel:
250-
sys.stderr.write("Error: Couldn't find code model generated by CMake\n")
254+
print("Error: Couldn't find code model generated by CMake", file=sys.stderr)
251255
env.Exit(1)
252256

253257
target_configs = load_target_configurations(project_codemodel)
@@ -270,7 +274,7 @@ def get_app_defines(app_config: dict) -> list[tuple[str, str]]:
270274
# within this archive.
271275
mbed_ce_lib_path = pathlib.Path("$BUILD_DIR") / "mbed-os" / "libmbed-os.a"
272276
link_args = ["-Wl,--whole-archive", '"' + str(mbed_ce_lib_path) + '"', "-Wl,--no-whole-archive"]
273-
env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", str(mbed_ce_lib_path))
277+
_ = env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", str(mbed_ce_lib_path))
274278

275279
# Get other linker flags from Mbed. We want these to appear after the application objects and Mbed libraries
276280
# because they contain the C/C++ library link flags.
@@ -297,5 +301,5 @@ def get_app_defines(app_config: dict) -> list[tuple[str, str]]:
297301
# Note that this seems to execute CMake, causing the code model query to be re-done.
298302
# So, we have to do this after we are done using the results of said query.
299303
project_ld_script = generate_project_ld_script()
300-
env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", str(project_ld_script))
304+
_ = env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", str(project_ld_script))
301305
env.Append(LDSCRIPT_PATH=str(project_ld_script))

tools/python/mbed_platformio/cmake_to_scons_converter.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99

1010
import collections
1111
import pathlib
12-
from typing import TYPE_CHECKING
12+
import typing
13+
from typing import TYPE_CHECKING, Sequence
1314

14-
import click
15+
from click.parser import split_arg_string
1516

1617
if TYPE_CHECKING:
1718
from SCons.Environment import Base as Environment
@@ -151,15 +152,15 @@ def _get_flags_for_compile_group(compile_group_json: dict) -> list[str]:
151152
fragment = ccfragment.get("fragment", "").strip()
152153
if not fragment or fragment.startswith("-D"):
153154
continue
154-
flags.extend(click.parser.split_arg_string(fragment))
155+
flags.extend(split_arg_string(fragment))
155156
return flags
156157

157158

158-
def extract_flags(target_json: dict) -> dict[str, list[str]]:
159+
def extract_flags(target_json: dict) -> dict[str, list[str] | None]:
159160
"""
160161
Returns a dictionary with flags for SCons based on a given CMake target
161162
"""
162-
default_flags = collections.defaultdict(list)
163+
default_flags: dict[str, list[str]] = collections.defaultdict(list)
163164
for cg in target_json["compileGroups"]:
164165
default_flags[cg["language"]].extend(_get_flags_for_compile_group(cg))
165166

@@ -173,11 +174,11 @@ def extract_flags(target_json: dict) -> dict[str, list[str]]:
173174

174175
def find_included_files(environment: Environment) -> set[str]:
175176
"""
176-
Process a list of flags produced by extract_flags() to find files manually included by '-include'
177+
Process an environment produced by prepare_build_envs() to find files manually included by '-include'
177178
"""
178179
result = set()
179180
for flag_var in ["CFLAGS", "CXXFLAGS", "CCFLAGS"]:
180-
language_flags = environment.get(flag_var)
181+
language_flags = typing.cast(Sequence[str], environment.get(flag_var))
181182
for index in range(len(language_flags)):
182183
if language_flags[index] == "-include" and index < len(language_flags) - 1:
183184
result.add(language_flags[index + 1])
@@ -212,7 +213,7 @@ def extract_link_args(target_json: dict) -> list[str]:
212213
fragment_role = f.get("role", "").strip()
213214
if not fragment or not fragment_role:
214215
continue
215-
args = click.parser.split_arg_string(fragment)
216+
args = split_arg_string(fragment)
216217
if fragment_role == "flags":
217218
result.extend(args)
218219

tools/python/mbed_tools/build/_internal/config/assemble_build_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def make_relative_if_possible(path: Path) -> Path:
6565
def _assemble_config_from_sources(
6666
target_attributes: dict, mbed_lib_files: List[Path], mbed_app_file: Optional[Path] = None
6767
) -> Config:
68-
config = Config(source.prepare(target_attributes, source_name="target"))
68+
config = Config(**source.prepare(target_attributes, source_name="target"))
6969
previous_filter_data = None
7070
app_data = None
7171
if mbed_app_file:

tools/python/mbed_tools/build/_internal/config/config.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
#
55
"""Build configuration representation."""
66

7+
from __future__ import annotations
8+
79
import logging
810
import pathlib
911
from collections import UserDict
1012
from typing import Any, Hashable, Iterable, List
1113

14+
import typing_extensions
15+
1216
from mbed_tools.build._internal.config.source import ConfigSetting, Override
1317

1418
logger = logging.getLogger(__name__)
@@ -28,10 +32,11 @@ class Config(UserDict):
2832
# All paths will be relative to the Mbed program root directory, or absolute if outside said directory.
2933
json_sources: List[pathlib.Path]
3034

31-
def __init__(self) -> None:
32-
super().__init__()
35+
def __init__(self, **kwargs: dict[str, Any]) -> None:
3336
self.json_sources = []
37+
super().__init__(**kwargs)
3438

39+
@typing_extensions.override
3540
def __setitem__(self, key: Hashable, item: Any) -> None:
3641
"""Set an item based on its key."""
3742
if key == CONFIG_SECTION:

tools/python/mbed_tools/build/_internal/memory_banks.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
from typing import TYPE_CHECKING
99

1010
if TYPE_CHECKING:
11-
from typing import Any, NotRequired, TypedDict
11+
from typing import Any, TypedDict
12+
13+
from typing_extensions import NotRequired
1214

1315
import copy
1416
import logging

tools/python/mbed_tools/build/_internal/write_files.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ def write_file(file_path: pathlib.Path, file_contents: str) -> None:
2828
raise InvalidExportOutputDirectoryError(msg)
2929

3030
output_directory.mkdir(parents=True, exist_ok=True)
31-
file_path.write_text(file_contents)
31+
_ = file_path.write_text(file_contents)

tools/python/mbed_tools/build/config.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from typing import Any, Tuple
1010

1111
from mbed_tools.build._internal.cmake_file import render_mbed_config_cmake_template
12-
from mbed_tools.build._internal.config.assemble_build_config import Config, assemble_config
12+
from mbed_tools.build._internal.config.assemble_build_config import assemble_config
13+
from mbed_tools.build._internal.config.config import Config
1314
from mbed_tools.build._internal.memory_banks import incorporate_memory_bank_data_from_cmsis, process_memory_banks
1415
from mbed_tools.build._internal.write_files import write_file
1516
from mbed_tools.build.exceptions import MbedBuildError
@@ -43,7 +44,9 @@ def generate_config(target_name: str, toolchain: str, program: MbedProgram) -> T
4344
# Process memory banks and save JSON data for other tools (e.g. memap) to use
4445
memory_banks_json_content = process_memory_banks(config)
4546
program.files.cmake_build_dir.mkdir(parents=True, exist_ok=True)
46-
(program.files.cmake_build_dir / MEMORY_BANKS_JSON_FILE).write_text(json.dumps(memory_banks_json_content, indent=4))
47+
_ = (program.files.cmake_build_dir / MEMORY_BANKS_JSON_FILE).write_text(
48+
json.dumps(memory_banks_json_content, indent=4)
49+
)
4750

4851
cmake_file_contents = render_mbed_config_cmake_template(
4952
target_name=target_name, config=config, toolchain_name=toolchain

tools/python/mbed_tools/build/flash.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def _flash_dev(disk: pathlib.Path, image_path: pathlib.Path) -> None:
2020
disk: Device mount point.
2121
image_path: Image file to be copied to device.
2222
"""
23-
shutil.copy(image_path, disk, follow_symlinks=False)
23+
_ = shutil.copy(image_path, disk, follow_symlinks=False)
2424
if platform.system() != "Windows":
2525
os.sync()
2626

tools/python/mbed_tools/cli/cmsis_mcu_descr.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,24 +105,19 @@ def get_mcu_names_used_by_targets_json5() -> Set[str]:
105105
"""
106106
Accumulate set of all `device_name` properties used by all targets defined in targets.json5 and custom_targets.json/json5.
107107
"""
108+
LOGGER.info("Scanning targets.json5 for used MCU names...")
109+
json_contents = decode_json_file(TARGETS_JSON5_PATH)
110+
108111
# Search for files starting with "custom_targets" of type .json or .json5. Also exclude some folders like build and mbed-os
109112
exclude_dirs = ["build", "mbed-os", ".git"]
110113
file_pattern = r"custom_targets\.(json|json5)"
111-
custom_targets_file = find_json_files(PROJECT_ROOT, exclude_dirs, file_pattern)
114+
custom_targets_files = find_json_files(PROJECT_ROOT, exclude_dirs, file_pattern)
112115

113-
custom_targets_json_path = {}
114-
for file in custom_targets_file:
115-
if file.exists():
116-
custom_targets_json_path = file
117-
LOGGER.info(f"Custom_targets file detected - {custom_targets_json_path}")
116+
for file in custom_targets_files:
117+
LOGGER.info(f"Custom_targets file detected - {file}")
118+
json_contents.update(decode_json_file(file))
118119

119120
used_mcu_names = set()
120-
LOGGER.info("Scanning targets.json5 for used MCU names...")
121-
json_contents = decode_json_file(TARGETS_JSON5_PATH)
122-
if custom_targets_file:
123-
LOGGER.info("Scanning custom_targets.json/json5. for used MCU names...")
124-
json_contents.update(decode_json_file(custom_targets_json_path))
125-
126121
for target_details in json_contents.values():
127122
if "device_name" in target_details:
128123
used_mcu_names.add(target_details["device_name"])

0 commit comments

Comments
 (0)