trouble with cmd.Cmd and prompting

Cameron Simpson cs at
Mon Jan 2 19:33:15 EST 2017

I'm using cmd.Cmd to write a little FTP-like command line to interface to a 
storage system of mine and encountering weird behaviour. When I enter a command 
the next prompt appears _before_ the associated operation runs, or so it 

Look at this:

    [~/hg/css-venti(hg:venti)]fleet*> dev ./bin/vt -M -C - -S '[archive]' ftp ~/var/mnt/archive.vt
    stores = [< object at 0x10a564a90>]
    S = Store(MappingStore(ConfigWatcher('/Users/cameron/.vtrc')[archive]))
    ~/var/mnt/archive.vt:/> ls
    ~/var/mnt/archive.vt:/> precmd: line='ls'
    ---------- OnyX.dmg
    postcmd: stop=None, line='ls'

See how the cmd.Cmd prompt "~/var/mnt/archive.vt:/> " appears again right after 
the "ls", and before its output:

    precmd: line='ls'
    ---------- OnyX.dmg
    postcmd: stop=None, line='ls'

The precmd and postcmd lines are debugging, emitted from my precmd and postcmd 
methods in the subclass.

Has anyone seen this kind of thing before?

The salient parts of my class are shown below. It's not complete and I'm happy 
to post the full thing if people need to see it. The behaviour is the same 
regardless of the command; even an empty command shows it:

    ~/var/mnt/archive.vt:/> precmd: line=''
    postcmd: stop=None, line=''

    ~/var/mnt/archive.vt:/> precmd: line=''
    postcmd: stop=None, line=''

Any insights or suggestions welcome. Code below.

Cameron Simpson <cs at>

from cmd import Cmd
import readline

class FTP(Cmd):

  def __init__(self, D, sep=None, FS=None, prompt=None):
    self._prompt = prompt
    if sep is None:
      sep = '/' # NB: _not_ os.sep
    self.root = D
    self.cwd = D
    self.sep = sep
    self.fs = FS

  def _set_prompt(self):
    prompt = self._prompt
    pwd = '/' + self.op_pwd()
    self.prompt = ( pwd if prompt is None else ":".join( (prompt, pwd) ) ) + '> '

  def precmd(self, line):
    X("precmd: line=%r", line)
    return line

  def postcmd(self, stop, line):
    X("postcmd: stop=%s, line=%r", stop, line)
    return stop

  def emptyline(self):

  def do_EOF(self, args):
    ''' Quit on end of input.
    return True

  def do_quit(self, args):
    ''' Usage: quit
    return True

  def do_ls(self, args):
    ''' Usage: ls [paths...]
    argv = shlex.split(args)
    if not argv:
      argv = sorted(self.cwd.entries.keys())
    for name in argv:
      with Pfx(name):
        E, P, tail = resolve(self.cwd, name)
        if tail:
          error("not found: unresolved path elements: %r", tail)
          M = E.meta
          S = M.stat()
          u, g, perms = M.unix_perms
          typemode = M.unix_typemode
          typechar = ( '-' if typemode == stat.S_IFREG
                  else 'd' if typemode == stat.S_IFDIR
                  else 's' if typemode == stat.S_IFLNK
                  else '?'
          print("%s%s%s%s %s" % ( typechar,

More information about the Python-list mailing list