Files
Medios-Macina/cmdlet/__init__.py

97 lines
2.7 KiB
Python
Raw Normal View History

2025-11-25 20:09:33 -08:00
from __future__ import annotations
from typing import Any, Callable, Dict, Iterable, Sequence
from importlib import import_module as _import_module
# A cmdlet is a callable taking (result, args, config) -> int
Cmdlet = Callable[[Any, Sequence[str], Dict[str, Any]], int]
# Registry of command-name -> cmdlet function
REGISTRY: Dict[str, Cmdlet] = {}
2025-12-12 21:55:38 -08:00
def _normalize_cmd_name(name: str) -> str:
2025-12-29 17:05:03 -08:00
return str(name or "").replace("_", "-").lower().strip()
2025-12-12 21:55:38 -08:00
def register_callable(names: Iterable[str], fn: Cmdlet) -> Cmdlet:
"""Register a callable under one or more command names.
This is the single registration mechanism used by both:
- legacy function cmdlet (decorator form)
- class-based cmdlet (Cmdlet.register())
"""
for name in names:
key = _normalize_cmd_name(name)
if key:
REGISTRY[key] = fn
return fn
2025-11-25 20:09:33 -08:00
def register(names: Iterable[str]):
"""Decorator to register a function under one or more command names.
Usage:
2025-12-11 23:21:45 -08:00
@register(["add-tags"])
2025-11-25 20:09:33 -08:00
def _run(result, args, config) -> int: ...
"""
2025-12-29 17:05:03 -08:00
2025-11-25 20:09:33 -08:00
def _wrap(fn: Cmdlet) -> Cmdlet:
2025-12-12 21:55:38 -08:00
return register_callable(names, fn)
2025-12-29 17:05:03 -08:00
2025-11-25 20:09:33 -08:00
return _wrap
def get(cmd_name: str) -> Cmdlet | None:
2025-12-12 21:55:38 -08:00
return REGISTRY.get(_normalize_cmd_name(cmd_name))
2025-11-25 20:09:33 -08:00
# Dynamically import all cmdlet modules in this directory (ignore files starting with _ and __init__.py)
2025-12-12 21:55:38 -08:00
# cmdlet self-register when instantiated via their __init__ method
2025-11-25 20:09:33 -08:00
import os
2025-12-29 17:05:03 -08:00
2025-11-25 20:09:33 -08:00
cmdlet_dir = os.path.dirname(__file__)
for filename in os.listdir(cmdlet_dir):
2025-12-05 03:42:57 -08:00
if not (
2025-12-29 17:05:03 -08:00
filename.endswith(".py") and not filename.startswith("_") and filename != "__init__.py"
2025-11-25 20:09:33 -08:00
):
2025-12-05 03:42:57 -08:00
continue
mod_name = filename[:-3]
# Enforce Powershell-style two-word cmdlet naming (e.g., add_file, get_file)
2025-12-12 21:55:38 -08:00
# Skip native/utility scripts that are not cmdlet (e.g., adjective, worker, matrix, pipe)
2025-12-05 03:42:57 -08:00
if "_" not in mod_name:
continue
try:
2025-12-11 12:47:30 -08:00
_import_module(f".{mod_name}", __name__)
2025-12-05 03:42:57 -08:00
except Exception as e:
import sys
2025-12-29 17:05:03 -08:00
2025-12-05 03:42:57 -08:00
print(f"Error importing cmdlet '{mod_name}': {e}", file=sys.stderr)
continue
2025-12-12 21:55:38 -08:00
# Import and register native commands that are not considered cmdlet
2025-12-05 03:42:57 -08:00
try:
2025-12-12 21:55:38 -08:00
from cmdnat import register_native_commands as _register_native_commands
2025-12-29 17:05:03 -08:00
2025-12-05 03:42:57 -08:00
_register_native_commands(REGISTRY)
except Exception:
# Native commands are optional; ignore if unavailable
pass
2025-11-25 20:09:33 -08:00
2025-12-12 21:55:38 -08:00
# Import root-level modules that also register cmdlet
2025-11-25 20:09:33 -08:00
for _root_mod in ("select_cmdlet",):
try:
_import_module(_root_mod)
except Exception:
# Allow missing optional modules
continue
2025-12-12 21:55:38 -08:00
# Also import helper modules that register cmdlet
2025-11-25 20:09:33 -08:00
try:
2025-12-11 19:04:02 -08:00
import API.alldebrid as _alldebrid
2025-11-25 20:09:33 -08:00
except Exception:
pass