initial commit
This commit is contained in:
parent
a60ada0ba3
commit
4015de11f7
|
@ -0,0 +1,11 @@
|
||||||
|
from typing import Optional
|
||||||
|
from . import mappers
|
||||||
|
|
||||||
|
MAPPERS = {"curl": mappers.CurlMapper,
|
||||||
|
"xargs": mappers.XargsMapper}
|
||||||
|
|
||||||
|
|
||||||
|
def convert_command(command: list[str], input_name: Optional[str]):
|
||||||
|
mapper_cls = MAPPERS.get(command[0], mappers.ShellMapper)
|
||||||
|
mapper = mapper_cls(command)
|
||||||
|
return mapper.generate(input_name, ["bytes"])
|
|
@ -0,0 +1,58 @@
|
||||||
|
import shlex
|
||||||
|
from textwrap import indent
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from . import convert
|
||||||
|
|
||||||
|
FILE_START = """\
|
||||||
|
# generated with hellpipe
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
FILE_END = """
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()"""
|
||||||
|
|
||||||
|
INPUTCMD = """curl "https://httpbin.org/get?test=123" | jq ".headers|keys[]" -r | xargs -L 1 echo"""
|
||||||
|
|
||||||
|
|
||||||
|
def split_commands(tokens):
|
||||||
|
cur_command = []
|
||||||
|
for tok in tokens:
|
||||||
|
if tok == "|":
|
||||||
|
yield cur_command
|
||||||
|
cur_command = []
|
||||||
|
continue
|
||||||
|
|
||||||
|
cur_command.append(tok)
|
||||||
|
yield cur_command
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
command = sys.argv[1] if len(sys.argv) > 1 else INPUTCMD
|
||||||
|
oneliner = shlex.shlex(INPUTCMD, posix=True, punctuation_chars=True)
|
||||||
|
stages = []
|
||||||
|
|
||||||
|
last_stage = None
|
||||||
|
for com in split_commands(oneliner):
|
||||||
|
if not last_stage:
|
||||||
|
input_name = None
|
||||||
|
else:
|
||||||
|
input_name = last_stage.output_name
|
||||||
|
stage = convert.convert_command(com, input_name)
|
||||||
|
stages.append(stage)
|
||||||
|
last_stage = stage
|
||||||
|
|
||||||
|
for stage in stages:
|
||||||
|
print("\n".join(stage.imports))
|
||||||
|
|
||||||
|
print(FILE_START)
|
||||||
|
|
||||||
|
for stage in stages:
|
||||||
|
print(indent(stage.code, " "))
|
||||||
|
|
||||||
|
print(FILE_END)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -0,0 +1,108 @@
|
||||||
|
import argparse
|
||||||
|
import abc
|
||||||
|
from typing import List
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from textwrap import dedent
|
||||||
|
from urllib import parse as urlparse
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PipeEnd:
|
||||||
|
name: str
|
||||||
|
format: str
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ShellMapping:
|
||||||
|
code: str
|
||||||
|
output_name: str
|
||||||
|
output_format: str
|
||||||
|
input_format: str
|
||||||
|
imports: List[str]
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractMapper(abc.ABC):
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __init__(self, command: List[str]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_input_types(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def generate(self, input_name: str, output_formats: List[str]) -> ShellMapping:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CurlMapper(AbstractMapper):
|
||||||
|
def __init__(self, command):
|
||||||
|
parser = argparse.ArgumentParser("curl", exit_on_error=False)
|
||||||
|
parser.add_argument("url")
|
||||||
|
self._parsed = parser.parse_args(command[1:])
|
||||||
|
|
||||||
|
def get_input_types(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def generate(self, input_name: str, output_formats: List[str]) -> ShellMapping:
|
||||||
|
url = urlparse.urlparse(self._parsed.url)
|
||||||
|
query = urlparse.parse_qs(url.query)
|
||||||
|
url_no_qs = urlparse.urlunparse(url._replace(query=None))
|
||||||
|
code = dedent(f"""\
|
||||||
|
params = {query!r}
|
||||||
|
res = requests.get({url_no_qs!r}, params=params).text
|
||||||
|
""")
|
||||||
|
return ShellMapping(code=code, output_name="res", output_format="str", input_format="", imports=["import requests"])
|
||||||
|
|
||||||
|
|
||||||
|
class ShellMapper(AbstractMapper):
|
||||||
|
def __init__(self, command):
|
||||||
|
self.command = command
|
||||||
|
|
||||||
|
def get_input_types(self):
|
||||||
|
return ["bytes"]
|
||||||
|
|
||||||
|
def generate(self, input_name: str, output_formats: List[str]) -> ShellMapping:
|
||||||
|
out_var = f"out_{self.command[0]}" # name var after command
|
||||||
|
if input_name:
|
||||||
|
code = dedent(f"""\
|
||||||
|
proc = subprocess.Popen({self.command!r}, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=sys.stderr)
|
||||||
|
(stdout, _) = proc.communicate({input_name}.encode("utf-8"))
|
||||||
|
{out_var} = stdout.decode("utf-8")
|
||||||
|
""")
|
||||||
|
else:
|
||||||
|
code = f"""{out_var} = subprocess.run({self.command!r}, check=True, stdout=subprocess.PIPE, stderr=sys.stderr).stdout"""
|
||||||
|
|
||||||
|
return ShellMapping(
|
||||||
|
code=code,
|
||||||
|
imports=["import subprocess", "import sys"],
|
||||||
|
input_format="bytes",
|
||||||
|
output_format="bytes",
|
||||||
|
output_name=out_var,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class XargsMapper(AbstractMapper):
|
||||||
|
def __init__(self, command):
|
||||||
|
parser = argparse.ArgumentParser("xargs", exit_on_error=False)
|
||||||
|
parser.add_argument("command", nargs="+")
|
||||||
|
parser.add_argument("-L", type=int)
|
||||||
|
self._parsed = parser.parse_args(command[1:])
|
||||||
|
self.input_types = ["list", "bytes"]
|
||||||
|
|
||||||
|
def get_input_types(self):
|
||||||
|
return ["list", "bytes"]
|
||||||
|
|
||||||
|
def generate(self, input_name: str, output_formats: List[str]) -> ShellMapping:
|
||||||
|
imports = []
|
||||||
|
if self._parsed.L >= 1:
|
||||||
|
command_str = self._parsed.command
|
||||||
|
run_command = "print(i) # TODO"
|
||||||
|
if True:
|
||||||
|
input_name = f"shlex.split({input_name}, posix=True)"
|
||||||
|
imports.append("import shlex")
|
||||||
|
code = dedent(
|
||||||
|
f"""\
|
||||||
|
for i in {input_name}:
|
||||||
|
{run_command}"""
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
code = "# todo: shell stuff"
|
||||||
|
return ShellMapping(code=code, output_name="", output_format="", input_format="list", imports=imports)
|
|
@ -0,0 +1,14 @@
|
||||||
|
[tool.poetry]
|
||||||
|
name = "hellpipe"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
authors = ["Your Name <you@example.com>"]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.9"
|
||||||
|
|
||||||
|
[tool.poetry.dev-dependencies]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
Loading…
Reference in New Issue