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