esp-idf/tools/idf_py_actions
Frantisek Hrbata 0de3c3b572 fix: exit gracefully when process started via asyncio is terminated
Currently when process is started through asyncio Runner and it is termited
e.g. with SIGINT(ctrl+c) a traceback is printed instead of gracefully
exit.

Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x7fe980970900>
Traceback (most recent call last):
  File "/usr/lib64/python3.12/asyncio/base_subprocess.py", line 129, in __del__
    self.close()
  File "/usr/lib64/python3.12/asyncio/base_subprocess.py", line 107, in close
    proto.pipe.close()
  File "/usr/lib64/python3.12/asyncio/unix_events.py", line 568, in close
    self._close(None)
  File "/usr/lib64/python3.12/asyncio/unix_events.py", line 592, in _close
    self._loop.call_soon(self._call_connection_lost, exc)
  File "/usr/lib64/python3.12/asyncio/base_events.py", line 793, in call_soon
    self._check_closed()
  File "/usr/lib64/python3.12/asyncio/base_events.py", line 540, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

This is caused because asyncio Runner context in asyncio.run is closing the event
loop and if exception is unhandled in coroutine(run_command) the transport is not
closed before the even loop is closed and we get RuntimeError: Event loop is closed
in the transport __del__ function because it's trying to use the closed
even loop.

Let's catch asyncio.CancelledError in case the process we are trying to
read from is terminated, print message, let the asyncio finish and exit
gracefully.

Closes https://github.com/espressif/esp-idf/issues/13418

Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
2024-04-22 11:51:32 +02:00
..
2022-06-15 14:33:29 +02:00
2022-06-15 14:33:29 +02:00
2022-06-15 14:33:29 +02:00

idf.py extensions

Python modules (subdirectories and files) in this directory named [your_extension]_ext will be loaded as idf.py extensions. If you want to provide extra extensions just provide ; separated list of directories with extensions in IDF_EXTRA_ACTIONS_PATH. Extensions will be loaded in alphanumeric order. Command line arguments parsing and extension mechanism is implemented on top of Click (versions >=5.0 are supported).

They should define a function action_extensions(base_actions, project_path) where:

  • base_actions - dictionary with actions that are already available for idf.py
  • project_path - working dir, may be defaulted to os.getcwd()

This function have to return a dict with 3 possible keys:

{
    # Additional options that will be available from id
    "global_options": [{
        "names": ["--option-name"],
        "help": "Help for option --option-name.",
    }],
    # List of functions that will have access to full app context, and can mangle with arguments
    "global_action_callbacks": [global_callback],
    # Additional subcommands for idf.py
    "actions": {
        "subcommand_name": {
            "callback": subcommand_callback,
            "help": "Help for subcommand.",
        },
    },
}

Where function global_callback(ctx, global_args, tasks) accepts 3 arguments:

  • ctx - Click context
  • global_args - dictionary of all available global arguments
  • tasks - list of Task objects

And subcommand_callback(subcommand_name, ctx, args) accepts 3 arguments:

  • subcommand_name - name of subcommand
  • ctx - Click context
  • args - list of command's arguments