Skip to content

Instantly share code, notes, and snippets.

@snOm3ad
Created November 14, 2023 02:52
Show Gist options
  • Save snOm3ad/4b7a4d74fd88a92f56a2f45baa4b6886 to your computer and use it in GitHub Desktop.
Save snOm3ad/4b7a4d74fd88a92f56a2f45baa4b6886 to your computer and use it in GitHub Desktop.
Python utility orchestrator
#!/usr/bin/python3
import subprocess as sp
import os
import argparse
import sys
def clap(args, utils):
parser = argparse.ArgumentParser(prog='PyRun', description='Script orchestration utility.')
parser.add_argument('util', choices=utils, help='Script to be ran')
group = parser.add_mutually_exclusive_group()
group.add_argument('-s', '--silent', action='store_true', help='Suppress output from script, ignored if script failed (use -fs instead).')
group.add_argument('-fs', '--force-silent', action='store_true', help='Do not show output from script regardless of exit code.')
group.add_argument('--header', action='store_true', help='Include header specifying stream type.')
return parser.parse_known_args(args)
def walk_root_dir(root):
path = os.path
if not path.exists(root):
raise ValueError("NO")
utilities = []
def recurse(dir):
entries = os.listdir(dir)
for entry in entries:
# append current dir
entry = dir + entry
if path.isfile(entry):
filename = path.basename(entry)
if path.splitext(filename)[1] == ".py":
utilities.append(entry)
elif path.isdir(entry):
recurse(entry + '/')
recurse(root)
return utilities
def main():
if len(sys.argv) < 2:
raise ValueError("NO")
root_dir = os.getenv("PYSCRIPT_DIR")
ROOT_DIR = root_dir if root_dir[-1] == '/' else root_dir + '/'
utils_with_paths = walk_root_dir(ROOT_DIR)
utilities = { os.path.basename(util[:-3]): util for util in utils_with_paths }
utilities["list"] = ""
command, args = clap(sys.argv[1:], utilities.keys())
if command.util == "list":
for util in utilities.keys():
if util != "list":
print(util)
else:
filename = utilities[command.util]
with open(filename) as file:
lines = file.readlines()
# 012
proc = lines[0][2:].rstrip() # e.g. #!/usr/bin/python3
output = sp.run([proc, filename] + args, cwd=os.getcwd(), capture_output=True)
errors = output.stderr.decode('UTF-8').rstrip()
messages = output.stdout.decode('UTF-8').rstrip()
if output.returncode != 0 and not command.force_silent:
print(f'[ERROR]: {command.util} was not successfully executed.\nScript failed with message(s): "{errors}"', file=sys.stderr)
elif not command.silent and not command.force_silent:
# output management hell
title = "{p} {cmd}::{{sty}} {p}\n".format(p='=' * 10, cmd=command.util)
if len(messages) > 0:
outtitle = title.format(sty='stdout')
print(f"{outtitle if command.header else ''}{messages}")
if len(errors) > 0:
errtitle = title.format(sty='stderr')
print(f"{errtitle if command.header else ''}{errors}")
if __name__ == "__main__":
main()
@snOm3ad
Copy link
Author

snOm3ad commented Nov 14, 2023

Do you keep multiple versions of python installed in your computer? Do you keep separate virtual environments for each script containing vastly different set of dependencies?

Do you spread your python scripts across multiple directories?

Do you dislike the idea of having to pollute your PATH (and by extension shell config file(s)) with even more directories? Does the idea of having to maintain symbolic links makes you sick?

Then boy is this the script for you. Just pass in your whatever directory where there is a python script (via the PYSCRIPT_DIR environment variable) and let pyrun handle the rest. Note, however, that you must ensure that your scripts start with a shebang comment (i.e. #!/bin/python3).

CAVEAT: it currently only handles commands that are not interactive!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment