Add a `preuserinput` hook in `cmd.Cmd`

There are a few hooks in cmd.Cmd now, but I had an issue with gh-119824: Print stack entry when user input is needed by gaogaotiantian · Pull Request #119882 · python/cpython · GitHub. I want to print something only when user input is needed. I found that very difficult with cmd.Cmd. preloop is designed to make something happen before the cmdloop but we don’t even know if user input is needed in cmdloop due to the existence of cmdqueue. Having a special command appended at the end of queue kind of works (that’s the solution for now), but that has side effects - lastcmd is changed and you’ll have to expose an user-available command to do that.

I propose to add a preuserinput hook before the user needs to type something (or it needs to read from self.stdin, basically after cmdqueue is drained). It could either be a one time per loop trigger (that’s my use case) or an unconditional trigger before every user input (it’s easy in the subclass to make it single fire per loop, the hook itself has more flexibility).

It will be a simple addition to the cmdloop without any backwards compatibility issue. Performance should not be an issue because user input is needed anyway and that’s slow enough compared to a function call.

You can override the prompt attribute as a property and prepend your pre-user-input messages to the prompt:

import cmd

class Cmd(cmd.Cmd):
    @property
    def prompt(self):
        return 'Pre-input\n' + super().prompt

    def do_echo(self, message):
        print(message)

command = Cmd()
command.cmdqueue += 'echo foo', 'echo bar'
command.cmdloop()

Demo session:

foo
bar
Pre-input
(Cmd) echo baz
baz
Pre-input
(Cmd)