python-cmd2 / cmd2

cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python
https://cmd2.readthedocs.io/en/stable/
MIT License
610 stars 115 forks source link

CMD2 exits, if function returns value #1327

Closed brijeshsirpatil closed 3 weeks ago

brijeshsirpatil commented 1 month ago

If your function returns a value, the command loop is being terminated. In the example below, the function is returning integer value 25,when you try this code you will see that executing the command results in cmd loop being terminated.

This used to work previously, probably in older release of Python. I recently got the latest version 3.12.4 and I find that it does not work any more

`

import cmd2

class FirstApp(cmd2.Cmd):
    """A simple cmd2 application."""

    def do_hello_world(self, _: cmd2.Statement):
        self.poutput('Hello World')
        return 25

if __name__ == '__main__':
    import sys
    c = FirstApp()
    sys.exit(c.cmdloop())

`

The issue seems to be in the _cmd_loop() function, the line "while not stop :" is no longer working as intended. The 'stop' variable at this point is integer value 25 which is being converted to true and the loop exits.

`

def _cmdloop(self) -> None:
    """Repeatedly issue a prompt, accept input, parse an initial prefix
    off the received input, and dispatch to action methods, passing them
    the remainder of the line as argument.

    This serves the same role as cmd.cmdloop().
    """
    saved_readline_settings = None

    try:
        # Get sigint protection while we set up readline for cmd2
        with self.sigint_protection:
            saved_readline_settings = self._set_up_cmd2_readline()

        # Run startup commands
        stop = self.runcmds_plus_hooks(self._startup_commands)
        self._startup_commands.clear()

        while not stop :
           # Get commands from user
            try:
                line = self._read_command_line(self.prompt)
            except KeyboardInterrupt:
                self.poutput('^C')
                line = ''

            # Run the command along with all associated pre and post hooks
            stop = self.onecmd_plus_hooks(line)
    finally:
        # Get sigint protection while we restore readline settings
        with self.sigint_protection:
            if saved_readline_settings is not None:
                self._restore_readline(saved_readline_settings)

`

Python programming is not my day job, so don't know the proper way to fix this. My hack for now is to change the line of code to explicitly check for Boolean True value.

while not (stop == True):

anselor commented 1 month ago

If returning a value worked at any point in the past then that was a bug in cmd2. The while not stop behavior is consistent with the behavior of the python core library class cmd2 is based upon.

See: https://github.com/python/cpython/blob/main/Lib/cmd.py#L128

brijeshsirpatil commented 1 month ago

Then please ignore the comment about intended behavior. I used CMD2 in last job, wrote something 2 years ago where functions were returning data ( I forget the details). At a new job now, I am starting fresh, downloaded latest everything and ran into this issue. I am out of my depth here beyond observing the hack seems to work for the simple example.

kmvanbrunt commented 3 weeks ago

Python's core cmd library says "the return value is a flag indicating whether interpretation of commands by the interpreter should stop."

Because of this, cmd2 added a member called self.last_result.

# Stores results from the last command run to enable usage of results in Python shells and pyscripts
self.last_result: Any = None

All you have to do is populate self.last_result in your do_* command function and check it after the command function returns. All of our built-in commands actually do this.

brijeshsirpatil commented 3 weeks ago

Thank you this is helpful. I had not come across self.last_result, now that I know about it, I will use this feature to return values.