Skip to content

Commit 6048d26

Browse files
committed
Merge branch 'fix/import_error_msg_v5.4' into 'release/v5.4'
fix(tools): re-raise ImportError without module name (v5.4) See merge request espressif/esp-idf!35073
2 parents e1c0ad9 + 2151f23 commit 6048d26

File tree

1 file changed

+27
-16
lines changed

1 file changed

+27
-16
lines changed

tools/idf.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
11
#!/usr/bin/env python
22
#
3-
# SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
3+
# SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
44
#
55
# SPDX-License-Identifier: Apache-2.0
66
#
77
# 'idf.py' is a top-level config/build command line tool for ESP-IDF
88
#
99
# You don't have to use idf.py, you can use cmake directly
1010
# (or use cmake in an IDE)
11-
1211
# WARNING: we don't check for Python build-time dependencies until
1312
# check_environment() function below. If possible, avoid importing
1413
# any external libraries here - put in external script, or import in
1514
# their specific function instead.
1615
import codecs
1716
import json
1817
import locale
19-
import os
2018
import os.path
2119
import shlex
2220
import subprocess
2321
import sys
24-
from collections import Counter, OrderedDict, _OrderedDictKeysView
22+
from collections import Counter
23+
from collections import OrderedDict
24+
from collections.abc import KeysView
2525
from importlib import import_module
2626
from pkgutil import iter_modules
27-
from typing import Any, Callable, Dict, List, Optional, Union
27+
from typing import Any
28+
from typing import Callable
29+
from typing import Dict
30+
from typing import List
31+
from typing import Optional
32+
from typing import Union
2833

2934
# pyc files remain in the filesystem when switching between branches which might raise errors for incompatible
3035
# idf.py extensions. Therefore, pyc file generation is turned off:
@@ -39,13 +44,19 @@
3944
if os.getenv('IDF_COMPONENT_MANAGER') != '0':
4045
from idf_component_manager import idf_extensions
4146
except ImportError as e:
42-
# For example, importing click could cause this.
43-
print((f'Cannot import module "{e.name}". This usually means that "idf.py" was not '
47+
print((f'{e}\n'
48+
f'This usually means that "idf.py" was not '
4449
f'spawned within an ESP-IDF shell environment or the python virtual '
4550
f'environment used by "idf.py" is corrupted.\n'
4651
f'Please use idf.py only in an ESP-IDF shell environment. If problem persists, '
4752
f'please try to install ESP-IDF tools again as described in the Get Started guide.'),
4853
file=sys.stderr)
54+
if e.name is None:
55+
# The ImportError or ModuleNotFoundError might be raised without
56+
# specifying a module name. In this not so common situation, re-raise
57+
# the exception to print all the information that could assist in
58+
# identifying the problem.
59+
raise
4960

5061
sys.exit(1)
5162

@@ -128,7 +139,7 @@ def _safe_relpath(path: str, start: Optional[str]=None) -> str:
128139
return os.path.abspath(path)
129140

130141

131-
def init_cli(verbose_output: List=None) -> Any:
142+
def init_cli(verbose_output: Optional[List]=None) -> Any:
132143
# Click is imported here to run it after check_environment()
133144
import click
134145

@@ -194,7 +205,7 @@ def __init__(self, callback: Callable, name: str, aliases: List, dependencies: O
194205
self.action_args = action_args
195206
self.aliases = aliases
196207

197-
def __call__(self, context: click.core.Context, global_args: PropertyDict, action_args: Dict=None) -> None:
208+
def __call__(self, context: click.core.Context, global_args: PropertyDict, action_args: Optional[Dict]=None) -> None:
198209
if action_args is None:
199210
action_args = self.action_args
200211

@@ -295,7 +306,7 @@ class Scope(object):
295306

296307
SCOPES = ('default', 'global', 'shared')
297308

298-
def __init__(self, scope: Union['Scope', str]=None) -> None: # noqa: F821
309+
def __init__(self, scope: Optional[Union['Scope', str]]=None) -> None: # noqa: F821
299310
if scope is None:
300311
self._scope = 'default'
301312
elif isinstance(scope, str) and scope in self.SCOPES:
@@ -318,7 +329,7 @@ def __str__(self) -> str:
318329

319330
class Option(click.Option):
320331
"""Option that knows whether it should be global"""
321-
def __init__(self, scope: Union[Scope, str]=None, deprecated: Union[Dict, str, bool]=False, hidden: bool=False, **kwargs: str) -> None:
332+
def __init__(self, scope: Optional[Union[Scope, str]]=None, deprecated: Union[Dict, str, bool]=False, hidden: bool=False, **kwargs: str) -> None:
322333
"""
323334
Keyword arguments additional to Click's Option class:
324335
@@ -356,7 +367,7 @@ def get_help_record(self, ctx: click.core.Context) -> Any:
356367

357368
class CLI(click.MultiCommand):
358369
"""Action list contains all actions with options available for CLI"""
359-
def __init__(self, all_actions: Dict=None, verbose_output: List=None, help: str=None) -> None:
370+
def __init__(self, all_actions: Optional[Dict]=None, verbose_output: Optional[List]=None, help: Optional[str]=None) -> None:
360371
super(CLI, self).__init__(
361372
chain=True,
362373
invoke_without_command=True,
@@ -440,7 +451,7 @@ def get_command(self, ctx: click.core.Context, name: str) -> Optional[Action]:
440451
return Action(name=name, callback=callback.unwrapped_callback)
441452
return None
442453

443-
def _print_closing_message(self, args: PropertyDict, actions: _OrderedDictKeysView) -> None:
454+
def _print_closing_message(self, args: PropertyDict, actions: KeysView) -> None:
444455
# print a closing message of some kind,
445456
# except if any of the following actions were requested
446457
if any(t in str(actions) for t in ('flash', 'dfu', 'uf2', 'uf2-app', 'qemu')):
@@ -578,7 +589,7 @@ def execute_tasks(self, tasks: List, **kwargs: str) -> OrderedDict:
578589

579590
dependecies_processed = True
580591

581-
# If task have some dependecies they have to be executed before the task.
592+
# If task have some dependencies they have to be executed before the task.
582593
for dep in task.dependencies:
583594
if dep not in tasks_to_run.keys():
584595
# If dependent task is in the list of unprocessed tasks move to the front of the list
@@ -711,11 +722,11 @@ def parse_project_dir(project_dir: str) -> Any:
711722
return CLI(help=cli_help, verbose_output=verbose_output, all_actions=all_actions)
712723

713724

714-
def main(argv: List[Any] = None) -> None:
725+
def main(argv: Optional[List[Any]] = None) -> None:
715726
# Check the environment only when idf.py is invoked regularly from command line.
716727
checks_output = None if SHELL_COMPLETE_RUN else check_environment()
717728

718-
# Check existance of the current working directory to prevent exceptions from click cli.
729+
# Check existence of the current working directory to prevent exceptions from click cli.
719730
try:
720731
os.getcwd()
721732
except FileNotFoundError as e:

0 commit comments

Comments
 (0)