Refactor project structure: update .gitignore, and add base and utils modules for improved functionality
This commit is contained in:
parent
df67db7c00
commit
09b585d5f6
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +1,6 @@
|
||||
venv/
|
||||
__pycache__/
|
||||
build/
|
||||
nut.egg-info/
|
||||
nut.egg-info/
|
||||
*.zip
|
||||
*.nut
|
||||
@ -1,3 +0,0 @@
|
||||
"""nut package."""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
@ -1,5 +1,5 @@
|
||||
import sys
|
||||
|
||||
from .utils import zip_dir
|
||||
|
||||
def main() -> int:
|
||||
for arg in sys.argv[1:]:
|
||||
|
||||
58
nut/builtins/base.py
Normal file
58
nut/builtins/base.py
Normal file
@ -0,0 +1,58 @@
|
||||
import sys
|
||||
|
||||
class _Exit:
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
|
||||
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
|
||||
self.stack = []
|
||||
self.ctx = ctx if ctx is not None else {}
|
||||
|
||||
def start(self):
|
||||
while self.current is not None:
|
||||
try:
|
||||
self.loop()
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def loop(self):
|
||||
action = self.current(self.ctx)
|
||||
|
||||
if isinstance(action, _Jmp):
|
||||
self.current = action.target
|
||||
return
|
||||
|
||||
if isinstance(action, _Call):
|
||||
self.stack.append(self.current)
|
||||
self.current = action.target
|
||||
return
|
||||
|
||||
if action is _Call.RETURN:
|
||||
if not self.stack:
|
||||
self.current = None
|
||||
return
|
||||
self.current = self.stack.pop()
|
||||
return
|
||||
|
||||
if isinstance(action, _Exit):
|
||||
sys.exit(action.code)
|
||||
|
||||
print(f"Unknown action: {action}")
|
||||
sys.exit(1)
|
||||
96
nut/utils.py
Normal file
96
nut/utils.py
Normal file
@ -0,0 +1,96 @@
|
||||
import os
|
||||
import zipfile
|
||||
import json
|
||||
import fnmatch
|
||||
|
||||
|
||||
def _should_ignore(rel_path, patterns):
|
||||
"""
|
||||
Check if a file should be ignored based on patterns.
|
||||
"""
|
||||
rel_path = rel_path.replace("\\", "/")
|
||||
|
||||
for pattern in patterns:
|
||||
pattern = pattern.strip()
|
||||
|
||||
if not pattern:
|
||||
continue
|
||||
|
||||
if pattern.endswith("/"):
|
||||
if rel_path.startswith(pattern):
|
||||
return True
|
||||
|
||||
if fnmatch.fnmatch(rel_path, pattern):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def zip_dir(source_dir, output_zip_path, metadata=None, ignore_patterns=None):
|
||||
"""
|
||||
Compress a directory into a zip file with ignore support.
|
||||
|
||||
:param source_dir: path to folder
|
||||
:param output_zip_path: where to save zip
|
||||
:param metadata: dict -> stored as _meta.json inside zip
|
||||
:param ignore_patterns: list like gitignore ["*.log", "__pycache__/", "build/*"]
|
||||
"""
|
||||
ignore_patterns = ignore_patterns or []
|
||||
|
||||
with zipfile.ZipFile(output_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
for root, dirs, files in os.walk(source_dir):
|
||||
|
||||
rel_root = os.path.relpath(root, source_dir).replace("\\", "/")
|
||||
|
||||
# filter dirs in-place (important for performance)
|
||||
dirs[:] = [
|
||||
d for d in dirs
|
||||
if not _should_ignore(
|
||||
os.path.join(rel_root, d).replace("\\", "/"),
|
||||
ignore_patterns
|
||||
)
|
||||
]
|
||||
|
||||
for file in files:
|
||||
full_path = os.path.join(root, file)
|
||||
rel_path = os.path.relpath(full_path, source_dir).replace("\\", "/")
|
||||
|
||||
if _should_ignore(rel_path, ignore_patterns):
|
||||
continue
|
||||
|
||||
zipf.write(full_path, rel_path)
|
||||
|
||||
if metadata:
|
||||
zipf.writestr("_meta.json", json.dumps(metadata, indent=2))
|
||||
|
||||
|
||||
def unzip_to_dir(zip_path, output_dir):
|
||||
"""
|
||||
Extract zip file to a directory.
|
||||
"""
|
||||
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
||||
zipf.extractall(output_dir)
|
||||
|
||||
|
||||
def unzip_in_memory(zip_path):
|
||||
"""
|
||||
Read zip contents into memory only.
|
||||
|
||||
:return: dict {filename: bytes}, metadata separately if exists
|
||||
"""
|
||||
data = {}
|
||||
metadata = None
|
||||
|
||||
with zipfile.ZipFile(zip_path, 'r') as zipf:
|
||||
for name in zipf.namelist():
|
||||
content = zipf.read(name)
|
||||
|
||||
if name == "_meta.json":
|
||||
metadata = json.loads(content.decode())
|
||||
else:
|
||||
data[name] = content
|
||||
|
||||
return {
|
||||
"files": data,
|
||||
"metadata": metadata
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user