Skip to content

Commit 0864e17

Browse files
committed
feat(errors): Print errors to STDERR, catch KeyboardInterrupt
BREAKING CHANGE Closes espressif#981 Closes espressif#888 Closes espressif#934
1 parent dbf3d1c commit 0864e17

File tree

8 files changed

+47
-17
lines changed

8 files changed

+47
-17
lines changed

docs/en/migration-guide.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,20 @@ The ``--verify`` option for the :ref:`write_flash <write-flash>` command has bee
5656

5757
1. Remove all ``--verify`` arguments from existing ``write_flash`` commands.
5858
2. Update scripts/CI pipelines to remove ``--verify`` flags.
59+
60+
Error Output Handling
61+
*********************
62+
63+
In ``v5``, error handling and output behavior have been improved to provide better user experience and script compatibility.
64+
65+
**Key Changes:**
66+
67+
- All error messages, including fatal errors, are now printed to **STDERR** instead of STDOUT.
68+
- User keyboard interrupts (e.g., Ctrl+C) are caught and raise an exit code of 2 to indicate an operation interruption.
69+
- Error messages are displayed in **red text** for better visibility.
70+
- This change ensures that errors are not lost when STDOUT is filtered or redirected.
71+
72+
**Migration Steps:**
73+
74+
1. Update scripts that rely on parsing STDOUT for error messages to check STDERR instead.
75+
2. Ensure scripts handle non-zero exit codes correctly in the case of operations interrupted by the user.

espefuse/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import espefuse.efuse.esp32s3beta2 as esp32s3beta2_efuse
2626

2727
import esptool
28+
from esptool.logger import log
2829

2930
DefChip = namedtuple("DefChip", ["chip_name", "efuse_lib", "chip_class"])
3031

@@ -361,7 +362,10 @@ def _main():
361362
try:
362363
main()
363364
except esptool.FatalError as e:
364-
print("\nA fatal error occurred: %s" % e)
365+
log.error(f"\nA fatal error occurred: {e}")
366+
sys.exit(2)
367+
except KeyboardInterrupt:
368+
log.error("KeyboardInterrupt: Run cancelled by user.")
365369
sys.exit(2)
366370

367371

espsecure/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
2222
from cryptography.utils import int_to_bytes
2323

24+
from esptool.logger import log
25+
2426
import ecdsa
2527

2628
import esptool
@@ -1921,18 +1923,21 @@ def _main():
19211923
try:
19221924
main()
19231925
except esptool.FatalError as e:
1924-
print("\nA fatal error occurred: %s" % e)
1926+
log.error(f"\nA fatal error occurred: {e}")
19251927
sys.exit(2)
19261928
except ValueError as e:
19271929
try:
19281930
if [arg for arg in e.args if "Could not deserialize key data." in arg]:
1929-
print(
1931+
log.error(
19301932
"Note: This error originates from the cryptography module. "
19311933
"It is likely not a problem with espsecure, "
19321934
"please make sure you are using a compatible OpenSSL backend."
19331935
)
19341936
finally:
19351937
raise
1938+
except KeyboardInterrupt:
1939+
log.error("KeyboardInterrupt: Run cancelled by user.")
1940+
sys.exit(2)
19361941

19371942

19381943
if __name__ == "__main__":

esptool/__init__.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ def add_spi_flash_subparsers(
814814
if esp is None:
815815
raise FatalError(
816816
"Could not connect to an Espressif device "
817-
"on any of the %d available serial ports." % len(ser_list)
817+
f"on any of the {len(ser_list)} available serial ports."
818818
)
819819

820820
if esp.secure_download_mode:
@@ -1226,7 +1226,7 @@ def get_default_connected_device(
12261226
except (FatalError, OSError) as err:
12271227
if port is not None:
12281228
raise
1229-
log.print(f"{each_port} failed to connect: {err}")
1229+
log.error(f"{each_port} failed to connect: {err}")
12301230
if _esp and _esp._port:
12311231
_esp._port.close()
12321232
_esp = None
@@ -1338,23 +1338,26 @@ def _main():
13381338
try:
13391339
main()
13401340
except FatalError as e:
1341-
log.print(f"\nA fatal error occurred: {e}")
1341+
log.error(f"\nA fatal error occurred: {e}")
13421342
sys.exit(2)
13431343
except serial.serialutil.SerialException as e:
1344-
log.print(f"\nA serial exception error occurred: {e}")
1345-
log.print(
1344+
log.error(f"\nA serial exception error occurred: {e}")
1345+
log.error(
13461346
"Note: This error originates from pySerial. "
13471347
"It is likely not a problem with esptool, "
13481348
"but with the hardware connection or drivers."
13491349
)
1350-
log.print(
1350+
log.error(
13511351
"For troubleshooting steps visit: "
13521352
"https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html"
13531353
)
13541354
sys.exit(1)
13551355
except StopIteration:
1356-
log.print(traceback.format_exc())
1357-
log.print("A fatal error occurred: The chip stopped responding.")
1356+
log.error(traceback.format_exc())
1357+
log.error("A fatal error occurred: The chip stopped responding.")
1358+
sys.exit(2)
1359+
except KeyboardInterrupt:
1360+
log.error("KeyboardInterrupt: Run cancelled by user.")
13581361
sys.exit(2)
13591362

13601363

esptool/loader.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
try:
3333
import serial
3434
except ImportError:
35-
log.print(
36-
f"Pyserial is not installed for {sys.executable}. "
37-
"Check the README for installation instructions."
35+
log.error(
36+
f"PySerial is not installed for {sys.executable}. "
37+
"Check the documentation for installation instructions."
3838
)
3939
raise
4040

@@ -59,7 +59,7 @@
5959
try:
6060
import serial.tools.list_ports as list_ports
6161
except ImportError:
62-
log.print(
62+
log.error(
6363
f"The installed version ({serial.VERSION}) of pySerial appears to be too old "
6464
f"for esptool.py (Python interpreter {sys.executable}). "
6565
"Check the documentation for installation instructions."

test/test_espefuse.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ def _run_command(self, cmd, check_msg, ret_code):
161161
shell=False,
162162
stdin=subprocess.PIPE,
163163
stdout=subprocess.PIPE,
164+
stderr=subprocess.STDOUT,
164165
universal_newlines=True,
165166
)
166167
output, _ = p.communicate()

test/test_image_info.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def run_image_info(self, chip, file):
4545
print("\nExecuting {}".format(" ".join(cmd)))
4646

4747
try:
48-
output = subprocess.check_output(cmd)
48+
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
4949
output = output.decode("utf-8")
5050
print(output) # for more complete stdout logs on failure
5151
assert (

test/test_imagegen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def run_elf2image(self, chip, elf_path, version=None, extra_args=[]):
146146
cmd += [elf_path] + extra_args
147147
print("\nExecuting {}".format(" ".join(cmd)))
148148
try:
149-
output = subprocess.check_output(cmd)
149+
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
150150
output = output.decode("utf-8")
151151
print(output)
152152
assert (

0 commit comments

Comments
 (0)