morbin.morbin
1import argparse 2import shlex 3import subprocess 4from contextlib import contextmanager 5from dataclasses import dataclass 6 7from pathier import Pathier 8 9root = Pathier(__file__).parent 10 11 12@dataclass 13class Output: 14 """Dataclass representing the output of a terminal command. 15 16 #### Fields: 17 * `return_code` 18 * `stdout` 19 * `stderr`""" 20 21 return_code: int 22 stdout: str = "" 23 stderr: str = "" 24 25 26class Morbin: 27 def __init__(self, capture_output: bool = False, shell: bool = False): 28 """Command bindings should return an `Output` object. 29 30 If `capture_output` is `True` or the `capturing_output` context manager is used, 31 the command's output will be available via `Output.stdout` and `Output.stderr`. 32 33 This property can be used to parse and use the command output or to simply execute commands "silently". 34 35 The return code will also be available via `Output.return_code`. 36 37 If `shell` is `True`, commands will be executed in the system shell (necessary on Windows for builtin shell commands like `cd` and `dir`). 38 39 [Security concerns using shell = True](https://docs.python.org/3/library/subprocess.html#security-considerations) 40 41 """ 42 self.capture_output = capture_output 43 self.shell = shell 44 45 @property 46 def capture_output(self) -> bool: 47 """If `True`, member functions will return the generated `stdout` as a string, 48 otherwise they return the command's exit code as a string (so my type checker doesn't throw a fit about ints.).""" 49 return self._capture_output 50 51 @capture_output.setter 52 def capture_output(self, should_capture: bool): 53 self._capture_output = should_capture 54 55 @property 56 def shell(self) -> bool: 57 """If `True`, commands will be executed in the system shell.""" 58 return self._shell 59 60 @shell.setter 61 def shell(self, should_use: bool): 62 self._shell = should_use 63 64 @contextmanager 65 def capturing_output(self): 66 """Ensures `self.capture_output` is `True` while within the context. 67 68 Upon exiting the context, `self.capture_output` will be set back to whatever it was when the context was entered.""" 69 original_state = self.capture_output 70 self.capture_output = True 71 yield self 72 self.capture_output = original_state 73 74 def execute(self, program: str, args: str) -> Output: 75 command = [program] + shlex.split(args) 76 if self.capture_output: 77 output = subprocess.run( 78 command, 79 stdout=subprocess.PIPE, 80 stderr=subprocess.PIPE, 81 text=True, 82 shell=self.shell, 83 ) 84 return Output(output.returncode, output.stdout, output.stderr) 85 else: 86 output = subprocess.run(command, shell=self.shell) 87 return Output(output.returncode) 88 89 90def get_args() -> argparse.Namespace: 91 parser = argparse.ArgumentParser() 92 93 parser.add_argument( 94 "name", 95 type=str, 96 help=""" The program name to create a template subclass of Morbin for. """, 97 ) 98 args = parser.parse_args() 99 100 return args 101 102 103def main(args: argparse.Namespace | None = None): 104 if not args: 105 args = get_args() 106 template = (root / "template.py").read_text() 107 template = template.replace("Program", args.name.capitalize()).replace( 108 "program", args.name 109 ) 110 (Pathier.cwd() / f"{args.name}.py").write_text(template) 111 112 113if __name__ == "__main__": 114 main(get_args())
13@dataclass 14class Output: 15 """Dataclass representing the output of a terminal command. 16 17 #### Fields: 18 * `return_code` 19 * `stdout` 20 * `stderr`""" 21 22 return_code: int 23 stdout: str = "" 24 stderr: str = ""
Dataclass representing the output of a terminal command.
Fields:
return_code
stdout
stderr
27class Morbin: 28 def __init__(self, capture_output: bool = False, shell: bool = False): 29 """Command bindings should return an `Output` object. 30 31 If `capture_output` is `True` or the `capturing_output` context manager is used, 32 the command's output will be available via `Output.stdout` and `Output.stderr`. 33 34 This property can be used to parse and use the command output or to simply execute commands "silently". 35 36 The return code will also be available via `Output.return_code`. 37 38 If `shell` is `True`, commands will be executed in the system shell (necessary on Windows for builtin shell commands like `cd` and `dir`). 39 40 [Security concerns using shell = True](https://docs.python.org/3/library/subprocess.html#security-considerations) 41 42 """ 43 self.capture_output = capture_output 44 self.shell = shell 45 46 @property 47 def capture_output(self) -> bool: 48 """If `True`, member functions will return the generated `stdout` as a string, 49 otherwise they return the command's exit code as a string (so my type checker doesn't throw a fit about ints.).""" 50 return self._capture_output 51 52 @capture_output.setter 53 def capture_output(self, should_capture: bool): 54 self._capture_output = should_capture 55 56 @property 57 def shell(self) -> bool: 58 """If `True`, commands will be executed in the system shell.""" 59 return self._shell 60 61 @shell.setter 62 def shell(self, should_use: bool): 63 self._shell = should_use 64 65 @contextmanager 66 def capturing_output(self): 67 """Ensures `self.capture_output` is `True` while within the context. 68 69 Upon exiting the context, `self.capture_output` will be set back to whatever it was when the context was entered.""" 70 original_state = self.capture_output 71 self.capture_output = True 72 yield self 73 self.capture_output = original_state 74 75 def execute(self, program: str, args: str) -> Output: 76 command = [program] + shlex.split(args) 77 if self.capture_output: 78 output = subprocess.run( 79 command, 80 stdout=subprocess.PIPE, 81 stderr=subprocess.PIPE, 82 text=True, 83 shell=self.shell, 84 ) 85 return Output(output.returncode, output.stdout, output.stderr) 86 else: 87 output = subprocess.run(command, shell=self.shell) 88 return Output(output.returncode)
28 def __init__(self, capture_output: bool = False, shell: bool = False): 29 """Command bindings should return an `Output` object. 30 31 If `capture_output` is `True` or the `capturing_output` context manager is used, 32 the command's output will be available via `Output.stdout` and `Output.stderr`. 33 34 This property can be used to parse and use the command output or to simply execute commands "silently". 35 36 The return code will also be available via `Output.return_code`. 37 38 If `shell` is `True`, commands will be executed in the system shell (necessary on Windows for builtin shell commands like `cd` and `dir`). 39 40 [Security concerns using shell = True](https://docs.python.org/3/library/subprocess.html#security-considerations) 41 42 """ 43 self.capture_output = capture_output 44 self.shell = shell
Command bindings should return an Output
object.
If capture_output
is True
or the capturing_output
context manager is used,
the command's output will be available via Output.stdout
and Output.stderr
.
This property can be used to parse and use the command output or to simply execute commands "silently".
The return code will also be available via Output.return_code
.
If shell
is True
, commands will be executed in the system shell (necessary on Windows for builtin shell commands like cd
and dir
).
If True
, member functions will return the generated stdout
as a string,
otherwise they return the command's exit code as a string (so my type checker doesn't throw a fit about ints.).
65 @contextmanager 66 def capturing_output(self): 67 """Ensures `self.capture_output` is `True` while within the context. 68 69 Upon exiting the context, `self.capture_output` will be set back to whatever it was when the context was entered.""" 70 original_state = self.capture_output 71 self.capture_output = True 72 yield self 73 self.capture_output = original_state
Ensures self.capture_output
is True
while within the context.
Upon exiting the context, self.capture_output
will be set back to whatever it was when the context was entered.
75 def execute(self, program: str, args: str) -> Output: 76 command = [program] + shlex.split(args) 77 if self.capture_output: 78 output = subprocess.run( 79 command, 80 stdout=subprocess.PIPE, 81 stderr=subprocess.PIPE, 82 text=True, 83 shell=self.shell, 84 ) 85 return Output(output.returncode, output.stdout, output.stderr) 86 else: 87 output = subprocess.run(command, shell=self.shell) 88 return Output(output.returncode)
104def main(args: argparse.Namespace | None = None): 105 if not args: 106 args = get_args() 107 template = (root / "template.py").read_text() 108 template = template.replace("Program", args.name.capitalize()).replace( 109 "program", args.name 110 ) 111 (Pathier.cwd() / f"{args.name}.py").write_text(template)