Update api to use chain and call

Much sexier than __call__
This commit is contained in:
Ian Adam Naval 2015-02-26 15:37:08 -05:00
parent 2ed55a58d2
commit 5565f0c4ef
5 changed files with 93 additions and 64 deletions

View File

@ -1,52 +1,46 @@
from formatters import Printer
from io import StringIO from io import StringIO
class BaseCommand(object): 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 self.cmd_args = args
def __call__(self, *args, **kwargs): self.prev_cmd = None
raise NotImplementedError(
"BaseCommands must be callable and return a generator")
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): def chain(self, cmd):
return [] """Chains a command to another command, returning the other command"""
cmd.prev_cmd = self
return cmd
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 []
registered_cmds = [] registered_cmds = []
def register_cmd(cls): def register_cmd(cls):
"""Decorator for putting all of the commands in one nice place."""
registered_cmds.append(cls.__name__) registered_cmds.append(cls.__name__)
return cls return cls

View File

@ -1,19 +1,33 @@
from commands import BaseCommand, register_cmd from commands import BaseCommand, register_cmd
@register_cmd @register_cmd
class example_cmd(BaseCommand): 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(): def output_generator():
yield b'example' yield b'example'
yield b'command' yield b'command'
return output_generator() return output_generator
@register_cmd @register_cmd
class echo(BaseCommand): class echo(BaseCommand):
def __call__(self,*args,**kwargs): """Echoes anything from the command line arguments as well as input
def output_generator(): from the previous command."""
for line in self.cmd_args:
yield line.encode('utf-8') def __init__(self, args):
return output_generator() 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

View File

@ -1,23 +1,12 @@
class Formatter(object): from commands import BaseCommand
"""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")
class Printer(Formatter): class Printer(BaseCommand):
"""Simple formatter which accepts any object from the input """Simple formatter which accepts any object from the input
generator and simply prints it as if it were a string.""" 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: for line in input_generator:
print(str(line.decode('utf-8'))) print(str(line.decode('utf-8')))
return None return None

11
main.py
View File

@ -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 formatters import Printer
from example_cmd import example_cmd, echo from example_cmd import example_cmd, echo
@ -46,10 +47,10 @@ def handle_input(prompt=""):
else: else:
cmds = parse_cmds(raw) cmds = parse_cmds(raw)
cmds.append("Printer()") cmds.append("Printer()")
mangled_input = "" mangled_input = cmds[0]
for cmd in reversed(cmds): for cmd in cmds[1:]:
mangled_input += cmd + "(" mangled_input += ".chain(" + cmd + ")"
mangled_input += ")" * len(cmds) mangled_input += ".call()"
print("[DEBUG]: evaluating Python: ", mangled_input) print("[DEBUG]: evaluating Python: ", mangled_input)
return mangled_input return mangled_input

31
raw_commands.py Normal file
View 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 []