dsf
This commit is contained in:
9
medeia_macina/__init__.py
Normal file
9
medeia_macina/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
"""Top-level package for Medeia-Macina.
|
||||
|
||||
This package provides the `cli_entry` module which exposes the `main()` entry
|
||||
point used by command-line launchers.
|
||||
"""
|
||||
|
||||
__all__ = ["cli_entry"]
|
||||
|
||||
__version__ = "0.1.0"
|
||||
245
medeia_macina/cli_entry.py
Normal file
245
medeia_macina/cli_entry.py
Normal file
@@ -0,0 +1,245 @@
|
||||
"""CLI entrypoint module compatible with console scripts.
|
||||
|
||||
This wraps the existing `medeia_entry.py` runner so installers can set
|
||||
entry points to `medeia_macina.cli_entry:main`.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, List, Tuple
|
||||
import sys
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
|
||||
|
||||
def _parse_mode_and_strip_args(args: List[str]) -> Tuple[Optional[str], List[str]]:
|
||||
"""Parse --gui/--cli/--mode flags and return (mode, cleaned_args).
|
||||
|
||||
The function removes any mode flags from the argument list so the selected
|
||||
runner can receive the remaining arguments untouched.
|
||||
|
||||
Supported forms:
|
||||
--gui, -g, --gui=true
|
||||
--cli, -c, --cli=true
|
||||
--mode=gui|cli
|
||||
--mode gui|cli
|
||||
|
||||
Raises ValueError on conflicting or invalid flags.
|
||||
"""
|
||||
mode: Optional[str] = None
|
||||
out: List[str] = []
|
||||
i = 0
|
||||
while i < len(args):
|
||||
a = args[i]
|
||||
la = a.lower()
|
||||
|
||||
# --gui / -g
|
||||
if la in ("--gui", "-g"):
|
||||
if mode and mode != "gui":
|
||||
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
|
||||
mode = "gui"
|
||||
i += 1
|
||||
continue
|
||||
if la.startswith("--gui="):
|
||||
val = la.split("=", 1)[1]
|
||||
if val and val not in ("0", "false", "no", "off"):
|
||||
if mode and mode != "gui":
|
||||
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
|
||||
mode = "gui"
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# --cli / -c
|
||||
if la in ("--cli", "-c"):
|
||||
if mode and mode != "cli":
|
||||
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
|
||||
mode = "cli"
|
||||
i += 1
|
||||
continue
|
||||
if la.startswith("--cli="):
|
||||
val = la.split("=", 1)[1]
|
||||
if val and val not in ("0", "false", "no", "off"):
|
||||
if mode and mode != "cli":
|
||||
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
|
||||
mode = "cli"
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# --mode
|
||||
if la.startswith("--mode="):
|
||||
val = la.split("=", 1)[1]
|
||||
val = val.lower()
|
||||
if val not in ("gui", "cli"):
|
||||
raise ValueError("--mode must be 'gui' or 'cli'")
|
||||
if mode and mode != val:
|
||||
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
|
||||
mode = val
|
||||
i += 1
|
||||
continue
|
||||
if la == "--mode":
|
||||
if i + 1 >= len(args):
|
||||
raise ValueError("--mode requires a value ('gui' or 'cli')")
|
||||
val = args[i + 1].lower()
|
||||
if val not in ("gui", "cli"):
|
||||
raise ValueError("--mode must be 'gui' or 'cli'")
|
||||
if mode and mode != val:
|
||||
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
|
||||
mode = val
|
||||
i += 2
|
||||
continue
|
||||
|
||||
# Not a mode flag; keep it
|
||||
out.append(a)
|
||||
i += 1
|
||||
|
||||
return mode, out
|
||||
|
||||
|
||||
def _import_medeia_entry_module():
|
||||
"""Import and return the top-level 'medeia_entry' module.
|
||||
|
||||
This attempts a regular import first. If that fails with ImportError it will
|
||||
try a few fallbacks useful for editable installs and running directly from
|
||||
the repository (searching for .egg-link, walking parents, or checking CWD).
|
||||
"""
|
||||
try:
|
||||
return importlib.import_module("medeia_entry")
|
||||
except ImportError:
|
||||
# Try to find the project root next to this installed package
|
||||
pkg_dir = Path(__file__).resolve().parent
|
||||
|
||||
# 1) Look for an .egg-link that points to the project root
|
||||
try:
|
||||
for egg in pkg_dir.glob("*.egg-link"):
|
||||
try:
|
||||
project_root = egg.read_text().splitlines()[0].strip()
|
||||
if project_root:
|
||||
candidate = Path(project_root) / "medeia_entry.py"
|
||||
if candidate.exists():
|
||||
if str(Path(project_root)) not in sys.path:
|
||||
sys.path.insert(0, str(Path(project_root)))
|
||||
return importlib.import_module("medeia_entry")
|
||||
except Exception:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2) Walk upwards looking for a top-level 'medeia_entry.py'
|
||||
for parent in pkg_dir.parents:
|
||||
candidate = parent / "medeia_entry.py"
|
||||
if candidate.exists():
|
||||
if str(parent) not in sys.path:
|
||||
sys.path.insert(0, str(parent))
|
||||
return importlib.import_module("medeia_entry")
|
||||
|
||||
# 3) Check current working directory
|
||||
candidate = Path.cwd() / "medeia_entry.py"
|
||||
if candidate.exists():
|
||||
if str(Path.cwd()) not in sys.path:
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
return importlib.import_module("medeia_entry")
|
||||
|
||||
raise ImportError(
|
||||
"Could not import 'medeia_entry'. Ensure the project was installed properly or run from the repo root."
|
||||
)
|
||||
|
||||
|
||||
def _run_cli(clean_args: List[str]) -> int:
|
||||
"""Run the CLI runner (MedeiaCLI) with cleaned argv list."""
|
||||
try:
|
||||
sys.argv[1:] = list(clean_args)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
mod = _import_medeia_entry_module()
|
||||
try:
|
||||
MedeiaCLI = getattr(mod, "MedeiaCLI")
|
||||
except AttributeError:
|
||||
raise ImportError("Imported module 'medeia_entry' does not define 'MedeiaCLI'")
|
||||
|
||||
try:
|
||||
app = MedeiaCLI()
|
||||
app.run()
|
||||
return 0
|
||||
except SystemExit as exc:
|
||||
return int(getattr(exc, "code", 0) or 0)
|
||||
|
||||
|
||||
def _run_gui(clean_args: List[str]) -> int:
|
||||
"""Run the TUI runner (PipelineHubApp).
|
||||
|
||||
The TUI is imported lazily; if Textual or the TUI code is unavailable we
|
||||
give a helpful error message and exit non‑zero.
|
||||
"""
|
||||
try:
|
||||
tui_mod = importlib.import_module("TUI.tui")
|
||||
except Exception as exc:
|
||||
print(
|
||||
"Error: Unable to import TUI (Textual may not be installed):",
|
||||
exc,
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
try:
|
||||
PipelineHubApp = getattr(tui_mod, "PipelineHubApp")
|
||||
except AttributeError:
|
||||
print("Error: 'TUI.tui' does not expose 'PipelineHubApp'", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
try:
|
||||
app = PipelineHubApp()
|
||||
app.run()
|
||||
return 0
|
||||
except SystemExit as exc:
|
||||
return int(getattr(exc, "code", 0) or 0)
|
||||
|
||||
|
||||
def main(argv: Optional[List[str]] = None) -> int:
|
||||
"""Entry point for console_scripts.
|
||||
|
||||
Accepts an optional argv list (useful for testing). Mode flags are parsed
|
||||
and removed before dispatching to the selected runner.
|
||||
"""
|
||||
args = list(argv) if argv is not None else list(sys.argv[1:])
|
||||
|
||||
try:
|
||||
mode, clean_args = _parse_mode_and_strip_args(args)
|
||||
except ValueError as exc:
|
||||
print(f"Error parsing mode flags: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
# If GUI requested, delegate directly (GUI may decide to honor any args itself)
|
||||
if mode == "gui":
|
||||
return _run_gui(clean_args)
|
||||
|
||||
# Support quoting a pipeline (or even a single full command) on the command line.
|
||||
#
|
||||
# - If the user provides a single argument that contains a pipe character,
|
||||
# treat it as a pipeline and rewrite the args to call the internal `pipeline`
|
||||
# subcommand so existing CLI pipeline handling is used.
|
||||
#
|
||||
# - If the user provides a single argument that contains whitespace but no pipe,
|
||||
# expand it into argv tokens (PowerShell commonly encourages quoting strings).
|
||||
#
|
||||
# Examples:
|
||||
# mm "download-media <url> | add-tag 'x' | add-file -store local"
|
||||
# mm "download-media '<url>' -query 'format:720p' -path 'C:\\out'"
|
||||
if len(clean_args) == 1:
|
||||
single = clean_args[0]
|
||||
if "|" in single and not single.startswith("-"):
|
||||
clean_args = ["pipeline", "--pipeline", single]
|
||||
elif (not single.startswith("-")) and any(ch.isspace() for ch in single):
|
||||
try:
|
||||
expanded = shlex.split(single, posix=True)
|
||||
if expanded:
|
||||
clean_args = list(expanded)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Default to CLI if --cli is requested or no explicit mode provided.
|
||||
return _run_cli(clean_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user