Repository: kedro-org/kedro
File: kedro/framework/cli/cli.py (Python)

Code before:
                hint = (
                    click.style(
                        "\nHint: Kedro is looking for a file called ", fg=BRIGHT_BLACK
                    )
                    + click.style("'pyproject.toml", fg="magenta")
                    + click.style(
                        ", is one present in your current working directory?",
                        fg=BRIGHT_BLACK,
                    )
                )
                click.echo(message)
                click.echo(hint)
            sys.exit(exc.code)
        except Exception as error:
            logger.error(f"An error has occurred: {error}")
            exit_code = 1
            self._cli_hook_manager.hook.after_command_run(
                project_metadata=self._metadata, command_args=args, exit_code=1
            )
            hook_called = True
        finally:
            if not hook_called:
                self._cli_hook_manager.hook.after_command_run(
                    project_metadata=self._metadata, command_args=args, exit_code=0
                )
            sys.exit(exit_code)

    @property
    def global_groups(self) -> Sequence[click.MultiCommand]:
        """Property which loads all global command groups from plugins and
        combines them with the built-in ones (eventually overriding the
        built-in ones if they are redefined by plugins).
        """
        return [*load_entry_points("global"), cli, global_commands]

    @property
    def project_groups(self) -> Sequence[click.MultiCommand]:
        """Property which loads all project command groups from the
        project and the plugins, then combines them with the built-in ones.
        Built-in commands can be overridden by plugins, which can be
        overridden by a custom project cli.py.
        See https://kedro.readthedocs.io/en/stable/extend_kedro/common_use_cases.html#use-case-3-how-to-add-or-modify-cli-commands
        on how to add this.
        """
        if not self._metadata:
            return []

        plugins = load_entry_points("project")

        try:
            project_cli = importlib.import_module(f"{self._metadata.package_name}.cli")

Review comment (bug):
Is this `sys.exit` necessary? I think there is a high chance this will cause problem to use kedro CLI as `sys.exit` would terminate the program eagerly.

https://github.com/kedro-org/kedro/pull/4026 is trying to change `click` to `standalone=False` to make CLI

https://stackoverflow.com/questions/60780051/python-click-command-exit-flow

This [issue](https://github.com/kedro-org/kedro/issues/3237) document the previous investigation done by @antonymilne and explains more in depth.

Code after:
                    + click.style(
                        ", is one present in your current working directory?",
                        fg=BRIGHT_BLACK,
                    )
                )
                click.echo(message)
                click.echo(hint)
                sys.exit(exc.code)
        except Exception as error:
            logger.error(f"An error has occurred: {error}")
            self._cli_hook_manager.hook.after_command_run(
                project_metadata=self._metadata, command_args=args, exit_code=1
            )
            hook_called = True
            sys.exit(1)
        finally:
            if not hook_called:
                self._cli_hook_manager.hook.after_command_run(
                    project_metadata=self._metadata, command_args=args, exit_code=0
                )

    @property
    def global_groups(self) -> Sequence[click.MultiCommand]:
        """Property which loads all global command groups from plugins and
        combines them with the built-in ones (eventually overriding the
        built-in ones if they are redefined by plugins).
        """
        return [*load_entry_points("global"), cli, global_commands]

    @property
    def project_groups(self) -> Sequence[click.MultiCommand]:
        """Property which loads all project command groups from the
        project and the plugins, then combines them with the built-in ones.
        Built-in commands can be overridden by plugins, which can be
        overridden by a custom project cli.py.
        See https://kedro.readthedocs.io/en/stable/extend_kedro/common_use_cases.html#use-case-3-how-to-add-or-modify-cli-commands
        on how to add this.
        """
        if not self._metadata:
            return []

        plugins = load_entry_points("project")

        try:
            project_cli = importlib.import_module(f"{self._metadata.package_name}.cli")
            # fail gracefully if cli.py does not exist
        except ModuleNotFoundError:
            # return only built-in commands and commands from plugins
            # (plugins can override built-in commands)
            return [*plugins, project_commands]