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)