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
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 aThreadedCompleter
. 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 isNone
, 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
) todata['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