Update api to use chain and call
Much sexier than __call__
This commit is contained in:
parent
2ed55a58d2
commit
5565f0c4ef
68
commands.py
68
commands.py
@ -1,52 +1,46 @@
|
||||
from formatters import Printer
|
||||
from io import StringIO
|
||||
|
||||
|
||||
class BaseCommand(object):
|
||||
def __init__(self, args):
|
||||
"""Commands can be used to chain the execution of multiple programs
|
||||
together. You can chain multiple commands together using commands
|
||||
|
||||
For example:
|
||||
ls = RawCommand(["ls"])
|
||||
grep = RawCommand(["grep", "potato"])
|
||||
printer = Printer()
|
||||
ls.chain(grep).chain(printer).call()
|
||||
"""
|
||||
|
||||
def __init__(self, args=None):
|
||||
self.cmd_args = args
|
||||
def __call__(self, *args, **kwargs):
|
||||
raise NotImplementedError(
|
||||
"BaseCommands must be callable and return a generator")
|
||||
self.prev_cmd = None
|
||||
|
||||
def call(self, *args, **kwargs):
|
||||
"""Implicitly calls any chained commands, returning a function
|
||||
to make an input generator."""
|
||||
raise NotImplementedError("Must implement call")
|
||||
|
||||
class NoneCommand(BaseCommand):
|
||||
def get_input_generator(self):
|
||||
"""Gets the input generator from the previous command, if it
|
||||
exists. If it doesn't exist, we just return an empy list so that
|
||||
when you iterate over it, it does nothing."""
|
||||
if self.prev_cmd is not None:
|
||||
make_input_generator = self.prev_cmd.call()
|
||||
input_generator = make_input_generator()
|
||||
else:
|
||||
input_generator = []
|
||||
return input_generator
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return []
|
||||
|
||||
|
||||
|
||||
class RawCommand(BaseCommand):
|
||||
"""Fallback raw command that just invokes an existing Unix utility program
|
||||
with the builtin subprocess module. Each output object is just a tree node
|
||||
whose data is a simple string."""
|
||||
|
||||
def __init__(self, cmd, input_generator=[]):
|
||||
self.input_generator = input_generator
|
||||
self.cmd = cmd
|
||||
|
||||
def __call__(self, input_generator=[], *args, **kwargs):
|
||||
import subprocess
|
||||
try:
|
||||
p = subprocess.Popen(self.cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
def output_generator():
|
||||
input_str = b""
|
||||
for line in input_generator:
|
||||
input_str += line + b'\n'
|
||||
outs, errs = p.communicate(input_str)
|
||||
if outs:
|
||||
yield outs
|
||||
|
||||
return output_generator()
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return []
|
||||
def chain(self, cmd):
|
||||
"""Chains a command to another command, returning the other command"""
|
||||
cmd.prev_cmd = self
|
||||
return cmd
|
||||
|
||||
|
||||
registered_cmds = []
|
||||
|
||||
def register_cmd(cls):
|
||||
"""Decorator for putting all of the commands in one nice place."""
|
||||
registered_cmds.append(cls.__name__)
|
||||
return cls
|
||||
|
@ -1,19 +1,33 @@
|
||||
from commands import BaseCommand, register_cmd
|
||||
|
||||
|
||||
@register_cmd
|
||||
class example_cmd(BaseCommand):
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Simple command that just returns 'example' and 'command'. Does
|
||||
nothing at all with the input."""
|
||||
|
||||
def call(self, *args, **kwargs):
|
||||
def output_generator():
|
||||
yield b'example'
|
||||
yield b'command'
|
||||
return output_generator()
|
||||
return output_generator
|
||||
|
||||
|
||||
@register_cmd
|
||||
class echo(BaseCommand):
|
||||
def __call__(self,*args,**kwargs):
|
||||
def output_generator():
|
||||
for line in self.cmd_args:
|
||||
yield line.encode('utf-8')
|
||||
return output_generator()
|
||||
"""Echoes anything from the command line arguments as well as input
|
||||
from the previous command."""
|
||||
|
||||
def __init__(self, args):
|
||||
super(echo, self).__init__()
|
||||
self.args = args
|
||||
|
||||
def call(self,*args,**kwargs):
|
||||
input_generator = self.get_input_generator()
|
||||
def output_generator():
|
||||
for args in self.args:
|
||||
yield args.encode("utf-8")
|
||||
for line in input_generator:
|
||||
yield line
|
||||
return output_generator
|
||||
|
||||
|
@ -1,23 +1,12 @@
|
||||
class Formatter(object):
|
||||
"""Formatters are callable objects who always accept a generator.
|
||||
The generator represents the output stream of an executing program.
|
||||
Formatters are special commands which produce no output themselves.
|
||||
Instead, they always write to the standard out of the program. """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Always require no arguments
|
||||
pass
|
||||
|
||||
def __call__(self, input_generator):
|
||||
raise NotImplementedError(
|
||||
"You must extend a Formatter and implement the __call__ method")
|
||||
from commands import BaseCommand
|
||||
|
||||
|
||||
class Printer(Formatter):
|
||||
class Printer(BaseCommand):
|
||||
"""Simple formatter which accepts any object from the input
|
||||
generator and simply prints it as if it were a string."""
|
||||
|
||||
def __call__(self, input_generator):
|
||||
def call(self):
|
||||
input_generator = self.get_input_generator()
|
||||
for line in input_generator:
|
||||
print(str(line.decode('utf-8')))
|
||||
return None
|
||||
|
11
main.py
11
main.py
@ -1,4 +1,5 @@
|
||||
from commands import RawCommand, registered_cmds
|
||||
from commands import registered_cmds
|
||||
from raw_commands import RawCommand
|
||||
from formatters import Printer
|
||||
from example_cmd import example_cmd, echo
|
||||
|
||||
@ -46,10 +47,10 @@ def handle_input(prompt=""):
|
||||
else:
|
||||
cmds = parse_cmds(raw)
|
||||
cmds.append("Printer()")
|
||||
mangled_input = ""
|
||||
for cmd in reversed(cmds):
|
||||
mangled_input += cmd + "("
|
||||
mangled_input += ")" * len(cmds)
|
||||
mangled_input = cmds[0]
|
||||
for cmd in cmds[1:]:
|
||||
mangled_input += ".chain(" + cmd + ")"
|
||||
mangled_input += ".call()"
|
||||
print("[DEBUG]: evaluating Python: ", mangled_input)
|
||||
return mangled_input
|
||||
|
||||
|
31
raw_commands.py
Normal file
31
raw_commands.py
Normal file
@ -0,0 +1,31 @@
|
||||
from formatters import Printer
|
||||
from commands import BaseCommand
|
||||
|
||||
|
||||
class RawCommand(BaseCommand):
|
||||
"""Fallback raw command that just invokes an existing Unix utility program
|
||||
with the builtin subprocess module. Each output object is just a tree node
|
||||
whose data is a simple string."""
|
||||
|
||||
def __init__(self, cmd):
|
||||
super(RawCommand, self).__init__()
|
||||
self.cmd = cmd
|
||||
|
||||
def call(self, *args, **kwargs):
|
||||
input_generator = self.get_input_generator()
|
||||
import subprocess
|
||||
try:
|
||||
p = subprocess.Popen(self.cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
def make_output_generator():
|
||||
input_str = b""
|
||||
for line in input_generator:
|
||||
input_str += line + b'\n'
|
||||
outs, errs = p.communicate(input_str)
|
||||
if outs:
|
||||
yield outs
|
||||
|
||||
return make_output_generator
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return []
|
Reference in New Issue
Block a user