Implement CLI framework and refactor main logic; add YAML support in utils

This commit is contained in:
Chipperfluff 2026-04-03 14:20:14 +02:00
parent b9761e5f13
commit c9176dbbce
5 changed files with 90 additions and 19 deletions

View File

@ -1,11 +1,28 @@
import sys
from .utils import zip_dir
from cli import CLI
def main() -> int:
for arg in sys.argv[1:]:
print(arg)
def main():
cli = CLI("nut")
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
def cmd_build(subcommands, flags):
print("BUILD")
print("sub:", subcommands)
print("flags:", flags)
return 0
if __name__ == "__main__":
raise SystemExit(main())

51
nut/cli.py Normal file
View 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
View File

View File

@ -8,15 +8,12 @@ class _Jump:
def __init__(self, target):
self.target = target
class _Jmp(_Jump):
pass
class _Call(_Jump):
RETURN = object()
class _VM:
def __init__(self, entry, ctx=None):
self.current = entry
@ -31,7 +28,6 @@ class _VM:
print(f"Error: {e}")
sys.exit(1)
def loop(self):
action = self.current(self.ctx)

View File

@ -2,9 +2,9 @@ import os
import zipfile
import json
import fnmatch
import yaml
def _should_ignore(rel_path, patterns):
def _should_ignore(rel_path: str, patterns: list) -> bool:
"""
Check if a file should be ignored based on patterns.
"""
@ -25,8 +25,7 @@ def _should_ignore(rel_path, patterns):
return False
def zip_dir(source_dir, output_zip_path, metadata=None, ignore_patterns=None):
def zip_dir(source_dir: str, output_zip_path: str, metadata: dict = None, ignore_patterns: list = None) -> None:
"""
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:
zipf.writestr("_meta.json", json.dumps(metadata, indent=2))
def unzip_to_dir(zip_path, output_dir):
def unzip_to_dir(zip_path: str, output_dir: str) -> None:
"""
Extract zip file to a directory.
"""
with zipfile.ZipFile(zip_path, 'r') as zipf:
zipf.extractall(output_dir)
def unzip_in_memory(zip_path):
def unzip_in_memory(zip_path: str) -> dict:
"""
Read zip contents into memory only.
@ -94,3 +91,13 @@ def unzip_in_memory(zip_path):
"files": data,
"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