Implement CLI framework and refactor main logic; add YAML support in utils
This commit is contained in:
parent
b9761e5f13
commit
c9176dbbce
@ -1,11 +1,28 @@
|
|||||||
import sys
|
from cli import CLI
|
||||||
from .utils import zip_dir
|
|
||||||
|
|
||||||
def main() -> int:
|
def main():
|
||||||
for arg in sys.argv[1:]:
|
cli = CLI("nut")
|
||||||
print(arg)
|
|
||||||
|
cli.command("run") \
|
||||||
|
.flag("debug", "d") \
|
||||||
|
.handle(cmd_run)
|
||||||
|
|
||||||
|
cli.command("build") \
|
||||||
|
.handle(cmd_build)
|
||||||
|
|
||||||
|
return cli.run()
|
||||||
|
|
||||||
|
def cmd_run(subcommands, flags):
|
||||||
|
print("RUN")
|
||||||
|
print("sub:", subcommands)
|
||||||
|
print("flags:", flags)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def cmd_build(subcommands, flags):
|
||||||
|
print("BUILD")
|
||||||
|
print("sub:", subcommands)
|
||||||
|
print("flags:", flags)
|
||||||
|
return 0
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
raise SystemExit(main())
|
raise SystemExit(main())
|
||||||
51
nut/cli.py
Normal file
51
nut/cli.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
class CLI:
|
||||||
|
def __init__(self, prog="nut"):
|
||||||
|
self.parser = argparse.ArgumentParser(prog=prog)
|
||||||
|
self.subparsers = self.parser.add_subparsers(dest="command", required=True)
|
||||||
|
|
||||||
|
def command(self, name):
|
||||||
|
parser = self.subparsers.add_parser(name)
|
||||||
|
|
||||||
|
cmd = _Command(parser)
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
args = self.parser.parse_args()
|
||||||
|
|
||||||
|
# extract structured data
|
||||||
|
sub_args = getattr(args, "_args", [])
|
||||||
|
flags = {
|
||||||
|
k: v for k, v in vars(args).items()
|
||||||
|
if k not in ("command", "func", "_args")
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.func(sub_args, flags)
|
||||||
|
|
||||||
|
class _Command:
|
||||||
|
def __init__(self, parser):
|
||||||
|
self.parser = parser
|
||||||
|
self.parser.set_defaults(func=self._missing)
|
||||||
|
|
||||||
|
# default catch-all args
|
||||||
|
self.parser.add_argument("_args", nargs="*")
|
||||||
|
|
||||||
|
def arg(self, name, **kwargs):
|
||||||
|
self.parser.add_argument(name, **kwargs)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def flag(self, name, short=None):
|
||||||
|
flags = [f"--{name}"]
|
||||||
|
if short:
|
||||||
|
flags.append(f"-{short}")
|
||||||
|
|
||||||
|
self.parser.add_argument(*flags, action="store_true")
|
||||||
|
return self
|
||||||
|
|
||||||
|
def handle(self, func):
|
||||||
|
self.parser.set_defaults(func=func)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def _missing(self, *_):
|
||||||
|
raise RuntimeError("No handler defined for command")
|
||||||
0
nut/templates/Nutfile
Normal file
0
nut/templates/Nutfile
Normal file
@ -8,15 +8,12 @@ class _Jump:
|
|||||||
def __init__(self, target):
|
def __init__(self, target):
|
||||||
self.target = target
|
self.target = target
|
||||||
|
|
||||||
|
|
||||||
class _Jmp(_Jump):
|
class _Jmp(_Jump):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class _Call(_Jump):
|
class _Call(_Jump):
|
||||||
RETURN = object()
|
RETURN = object()
|
||||||
|
|
||||||
|
|
||||||
class _VM:
|
class _VM:
|
||||||
def __init__(self, entry, ctx=None):
|
def __init__(self, entry, ctx=None):
|
||||||
self.current = entry
|
self.current = entry
|
||||||
@ -31,7 +28,6 @@ class _VM:
|
|||||||
print(f"Error: {e}")
|
print(f"Error: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def loop(self):
|
def loop(self):
|
||||||
action = self.current(self.ctx)
|
action = self.current(self.ctx)
|
||||||
|
|
||||||
23
nut/utils.py
23
nut/utils.py
@ -2,9 +2,9 @@ import os
|
|||||||
import zipfile
|
import zipfile
|
||||||
import json
|
import json
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
def _should_ignore(rel_path: str, patterns: list) -> bool:
|
||||||
def _should_ignore(rel_path, patterns):
|
|
||||||
"""
|
"""
|
||||||
Check if a file should be ignored based on patterns.
|
Check if a file should be ignored based on patterns.
|
||||||
"""
|
"""
|
||||||
@ -25,8 +25,7 @@ def _should_ignore(rel_path, patterns):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def zip_dir(source_dir: str, output_zip_path: str, metadata: dict = None, ignore_patterns: list = None) -> None:
|
||||||
def zip_dir(source_dir, output_zip_path, metadata=None, ignore_patterns=None):
|
|
||||||
"""
|
"""
|
||||||
Compress a directory into a zip file with ignore support.
|
Compress a directory into a zip file with ignore support.
|
||||||
|
|
||||||
@ -63,16 +62,14 @@ def zip_dir(source_dir, output_zip_path, metadata=None, ignore_patterns=None):
|
|||||||
if metadata:
|
if metadata:
|
||||||
zipf.writestr("_meta.json", json.dumps(metadata, indent=2))
|
zipf.writestr("_meta.json", json.dumps(metadata, indent=2))
|
||||||
|
|
||||||
|
def unzip_to_dir(zip_path: str, output_dir: str) -> None:
|
||||||
def unzip_to_dir(zip_path, output_dir):
|
|
||||||
"""
|
"""
|
||||||
Extract zip file to a directory.
|
Extract zip file to a directory.
|
||||||
"""
|
"""
|
||||||
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
||||||
zipf.extractall(output_dir)
|
zipf.extractall(output_dir)
|
||||||
|
|
||||||
|
def unzip_in_memory(zip_path: str) -> dict:
|
||||||
def unzip_in_memory(zip_path):
|
|
||||||
"""
|
"""
|
||||||
Read zip contents into memory only.
|
Read zip contents into memory only.
|
||||||
|
|
||||||
@ -94,3 +91,13 @@ def unzip_in_memory(zip_path):
|
|||||||
"files": data,
|
"files": data,
|
||||||
"metadata": metadata
|
"metadata": metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def read_yaml(path: str) -> dict:
|
||||||
|
"""
|
||||||
|
:param path: path to yaml file
|
||||||
|
:return: parsed dict
|
||||||
|
"""
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user