devshell.ptcmd API documentation
Description: This script takes the Cmd object from the built-in standard library, adds an input_method attribute, and replaces all calls to the standard input() …

# Module `devshell.ptcmd`

This script takes the Cmd object from the built-in standard library, adds an input_method attribute, and replaces all calls to the standard input() with calls to the input_method attribute

Expand source code

    """
    This script takes the Cmd object from the built-in standard library, adds an input_method attribute, and replaces all calls to the standard input() with calls to the input_method attribute
    """
    import ast, inspect, cmd, shlex
    from prompt_toolkit.completion import Completer, Completion
    from prompt_toolkit import PromptSession

    class PTCmd_Completer(Completer):
        def __init__(self,ptcmd):
            self.ptcmd = ptcmd
        def get_completions(self,document,complete_event):
            for suggestion in self.ptcmd.pt_complete(document,complete_event):
                #yield Completion(suggestion,start_position=0)
                try:
                    yield Completion(suggestion,-len(shlex.split(document.current_line_before_cursor)[-1]))
                except:
                    pass
    class PTCmd(cmd.Cmd):
        def __init__(self, completekey='tab', stdin=None, stdout=None,**psession_kwargs):
            super().__init__(completekey,stdin,stdout)
            psession_kwargs['completer'] = PTCmd_Completer(self)
            psession_kwargs['complete_while_typing'] = True
            self.psession = PromptSession(**psession_kwargs)
            self.input_method = self.psession.prompt
        def pt_complete(self, document,complete_event):
            origline = document.text
            line = origline.lstrip()
            stripped = len(origline) - len(line)
            begidx = document.cursor_position_col - stripped
            endidx = len(document.text) - stripped
            if begidx>0:
                cmd, args, foo = self.parseline(line)
                if cmd == '':
                    compfunc = self.completedefault
                else:
                    try:
                        compfunc = getattr(self, 'complete_' + cmd)
                    except AttributeError:
                        compfunc = self.completedefault
            else:
                compfunc = self.completenames
            self.completion_matches = compfunc(document.text, line, begidx, endidx)
            yield from self.completion_matches

    class SwitchInput(ast.NodeTransformer):
        def visit_Call(self,node):
            if isinstance(node.func,ast.Name) and node.func.id == 'input':
                load = ast.Load()
                return ast.Call(
                        func = ast.Attribute(
                                value=ast.Name(
                                    id='self',
                                    ctx=load,
                                    ),
                                attr='input_method',
                                ctx=load,
                            ),
                        args=node.args,
                        keywords=node.keywords,
                        )
            else:
                return node

    ptcmd_tree = ast.parse(inspect.getsource(PTCmd))
    cmd_tree = ast.fix_missing_locations(SwitchInput().visit(ast.parse(inspect.getsource(cmd.Cmd)))) #get a version that swaps all input(...) calls with self.input_method(...)

    #find the cmdloop function
    found = False
    for node in ast.walk(cmd_tree):
        if isinstance(node,ast.FunctionDef) and node.name == 'cmdloop':
            cmdloop_node = node
            found = True
            break
    assert(found)

    #find the PTCmd class
    found = False
    for node in ast.walk(ptcmd_tree):
        if isinstance(node,ast.ClassDef) and node.name == 'PTCmd':
            found = True
            ptcmd_node = node
            break
    assert(found)
    #add the cmdloop function to the class definition (overwrite inherited version)
    ptcmd_node.body.append(cmdloop_node)
    ptcmd_tree = ast.fix_missing_locations(ptcmd_tree)

    #Redefine the new class
    exec(compile(ptcmd_tree,'<ast>','exec'))

## Classes

`class PTCmd (completekey='tab', stdin=None, stdout=None, **psession_kwargs)`

  A simple framework for writing line-oriented command interpreters.

  These are often useful for test harnesses, administrative tools, and prototypes that will later be wrapped in a more sophisticated interface.

  A Cmd instance or subclass instance is a line-oriented interpreter framework. There is no good reason to instantiate Cmd itself; rather, it's useful as a superclass of an interpreter class you define yourself in order to inherit Cmd's methods and encapsulate action methods.

  Instantiate a line-oriented interpreter framework.

  The optional argument 'completekey' is the readline name of a completion key; it defaults to the Tab key. If completekey is not None and the readline module is available, command completion is done automatically. The optional arguments stdin and stdout specify alternate input and output file objects; if not specified, sys.stdin and sys.stdout are used.

  Expand source code

      class PTCmd(cmd.Cmd):
          def __init__(self, completekey='tab', stdin=None, stdout=None,**psession_kwargs):
              super().__init__(completekey,stdin,stdout)
              psession_kwargs['completer'] = PTCmd_Completer(self)
              psession_kwargs['complete_while_typing'] = True
              self.psession = PromptSession(**psession_kwargs)
              self.input_method = self.psession.prompt
          def pt_complete(self, document,complete_event):
              origline = document.text
              line = origline.lstrip()
              stripped = len(origline) - len(line)
              begidx = document.cursor_position_col - stripped
              endidx = len(document.text) - stripped
              if begidx>0:
                  cmd, args, foo = self.parseline(line)
                  if cmd == '':
                      compfunc = self.completedefault
                  else:
                      try:
                          compfunc = getattr(self, 'complete_' + cmd)
                      except AttributeError:
                          compfunc = self.completedefault
              else:
                  compfunc = self.completenames
              self.completion_matches = compfunc(document.text, line, begidx, endidx)
              yield from self.completion_matches

  ### Ancestors

  - cmd.Cmd

  ### Subclasses

  - DevshellCmd

  ### Methods

  `def cmdloop(self, intro=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.

  `def pt_complete(self, document, complete_event)`
`class PTCmd_Completer (ptcmd)`

  Base class for completer implementations.

  Expand source code

      class PTCmd_Completer(Completer):
          def __init__(self,ptcmd):
              self.ptcmd = ptcmd
          def get_completions(self,document,complete_event):
              for suggestion in self.ptcmd.pt_complete(document,complete_event):
                  #yield Completion(suggestion,start_position=0)
                  try:
                      yield Completion(suggestion,-len(shlex.split(document.current_line_before_cursor)[-1]))
                  except:
                      pass

  ### Ancestors

  - prompt_toolkit.completion.base.Completer

  ### Methods

  `def get_completions(self, document, complete_event)`

    This should be a generator that yields :class:`.Completion` instances.

    If the generation of completions is something expensive (that takes a lot of time), consider wrapping this `Completer` class in a `ThreadedCompleter`. In that case, the completer algorithm runs in a background thread and completions will be displayed as soon as they arrive.

    :param document: :class:`~prompt_toolkit.document.Document` instance. :param complete_event: :class:`.CompleteEvent` instance.

    Expand source code

        def get_completions(self,document,complete_event):
            for suggestion in self.ptcmd.pt_complete(document,complete_event):
                #yield Completion(suggestion,start_position=0)
                try:
                    yield Completion(suggestion,-len(shlex.split(document.current_line_before_cursor)[-1]))
                except:
                    pass

`class SwitchInput`

  A :class:`NodeVisitor` subclass that walks the abstract syntax tree and allows modification of nodes.

  The `NodeTransformer` will walk the AST and use the return value of the visitor methods to replace or remove the old node. If the return value of the visitor method is `None`, the node will be removed from its location, otherwise it is replaced with the return value. The return value may be the original node in which case no replacement takes place.

  Here is an example transformer that rewrites all occurrences of name lookups (`foo`) to `data['foo']`::

  class RewriteName(NodeTransformer):

         def visit_Name(self, node):
             return Subscript(
                 value=Name(id='data', ctx=Load()),
                 slice=Index(value=Str(s=node.id)),
                 ctx=node.ctx
             )

  Keep in mind that if the node you're operating on has child nodes you must either transform the child nodes yourself or call the :meth:`generic_visit` method for the node first.

  For nodes that were part of a collection of statements (that applies to all statement nodes), the visitor may also return a list of nodes rather than just a single node.

  Usually you use the transformer like this::

  node = YourTransformer().visit(node)

  Expand source code

      class SwitchInput(ast.NodeTransformer):
          def visit_Call(self,node):
              if isinstance(node.func,ast.Name) and node.func.id == 'input':
                  load = ast.Load()
                  return ast.Call(
                          func = ast.Attribute(
                                  value=ast.Name(
                                      id='self',
                                      ctx=load,
                                      ),
                                  attr='input_method',
                                  ctx=load,
                              ),
                          args=node.args,
                          keywords=node.keywords,
                          )
              else:
                  return node

  ### Ancestors

  - ast.NodeTransformer
  - ast.NodeVisitor

  ### Methods

  `def visit_Call(self, node)`
    Expand source code

        def visit_Call(self,node):
            if isinstance(node.func,ast.Name) and node.func.id == 'input':
                load = ast.Load()
                return ast.Call(
                        func = ast.Attribute(
                                value=ast.Name(
                                    id='self',
                                    ctx=load,
                                    ),
                                attr='input_method',
                                ctx=load,
                            ),
                        args=node.args,
                        keywords=node.keywords,
                        )
            else:
                return node