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
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

View File

@ -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

View File

@ -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
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 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
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 []