khh
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
This commit is contained in:
211
MPV/LUA/main.lua
211
MPV/LUA/main.lua
@@ -4,7 +4,7 @@ local msg = require 'mp.msg'
|
||||
|
||||
local M = {}
|
||||
|
||||
local MEDEIA_LUA_VERSION = '2025-12-18'
|
||||
local MEDEIA_LUA_VERSION = '2025-12-24'
|
||||
|
||||
-- Track whether uosc is available so menu calls don't fail with
|
||||
-- "Can't find script 'uosc' to send message to."
|
||||
@@ -159,6 +159,8 @@ local function write_temp_log(prefix, text)
|
||||
|
||||
local dir = ''
|
||||
-- Prefer repo-root Log/ for easier discovery.
|
||||
-- NOTE: Avoid spawning cmd.exe/sh just to mkdir on Windows/Linux; console flashes are
|
||||
-- highly undesirable. If the directory doesn't exist, we fall back to TEMP.
|
||||
do
|
||||
local function find_up(start_dir, relative_path, max_levels)
|
||||
local d = start_dir
|
||||
@@ -186,13 +188,6 @@ local function write_temp_log(prefix, text)
|
||||
local parent = cli:match('(.*)[/\\]') or ''
|
||||
if parent ~= '' then
|
||||
dir = utils.join_path(parent, 'Log')
|
||||
-- Best-effort create dir.
|
||||
local sep = package and package.config and package.config:sub(1, 1) or '/'
|
||||
if sep == '\\' then
|
||||
pcall(utils.subprocess, { args = { 'cmd.exe', '/c', 'mkdir "' .. dir .. '" 1>nul 2>nul' } })
|
||||
else
|
||||
pcall(utils.subprocess, { args = { 'sh', '-lc', 'mkdir -p ' .. string.format('%q', dir) .. ' >/dev/null 2>&1' } })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -207,7 +202,15 @@ local function write_temp_log(prefix, text)
|
||||
local path = utils.join_path(dir, name)
|
||||
local fh = io.open(path, 'w')
|
||||
if not fh then
|
||||
return nil
|
||||
-- If Log/ wasn't created (or is not writable), fall back to TEMP.
|
||||
local tmp = os.getenv('TEMP') or os.getenv('TMP') or ''
|
||||
if tmp ~= '' and tmp ~= dir then
|
||||
path = utils.join_path(tmp, name)
|
||||
fh = io.open(path, 'w')
|
||||
end
|
||||
if not fh then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
fh:write(text)
|
||||
fh:close()
|
||||
@@ -350,6 +353,30 @@ local function _is_windows()
|
||||
return sep == '\\'
|
||||
end
|
||||
|
||||
local function _resolve_python_exe(prefer_no_console)
|
||||
local python = (opts and opts.python_path) and tostring(opts.python_path) or 'python'
|
||||
if (not prefer_no_console) or (not _is_windows()) then
|
||||
return python
|
||||
end
|
||||
|
||||
local low = tostring(python):lower()
|
||||
if low == 'python' then
|
||||
return 'pythonw'
|
||||
end
|
||||
if low == 'python.exe' then
|
||||
return 'pythonw.exe'
|
||||
end
|
||||
if low:sub(-10) == 'python.exe' then
|
||||
local candidate = python:sub(1, #python - 10) .. 'pythonw.exe'
|
||||
if utils.file_info(candidate) then
|
||||
return candidate
|
||||
end
|
||||
return 'pythonw'
|
||||
end
|
||||
-- Already pythonw or some other launcher.
|
||||
return python
|
||||
end
|
||||
|
||||
local function _extract_target_from_memory_uri(text)
|
||||
if type(text) ~= 'string' then
|
||||
return nil
|
||||
@@ -475,10 +502,10 @@ end
|
||||
local function _get_current_item_is_image()
|
||||
local video_info = mp.get_property_native('current-tracks/video')
|
||||
if type(video_info) == 'table' then
|
||||
if video_info.image and not video_info.albumart then
|
||||
if video_info.image == true then
|
||||
return true
|
||||
end
|
||||
if video_info.image == false and video_info.albumart == true then
|
||||
if video_info.image == false then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -489,8 +516,6 @@ local function _get_current_item_is_image()
|
||||
return false
|
||||
end
|
||||
|
||||
-- Cover art / splash support disabled (removed per user request)
|
||||
|
||||
|
||||
local function _set_image_property(value)
|
||||
pcall(mp.set_property_native, 'user-data/mpv/image', value and true or false)
|
||||
@@ -789,7 +814,8 @@ local function _pick_folder_windows()
|
||||
-- Native folder picker via PowerShell + WinForms.
|
||||
local ps = [[Add-Type -AssemblyName System.Windows.Forms; $d = New-Object System.Windows.Forms.FolderBrowserDialog; $d.Description = 'Select download folder'; $d.ShowNewFolderButton = $true; if ($d.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { $d.SelectedPath }]]
|
||||
local res = utils.subprocess({
|
||||
args = { 'powershell', '-NoProfile', '-STA', '-ExecutionPolicy', 'Bypass', '-Command', ps },
|
||||
-- Hide the PowerShell console window (dialog still shows).
|
||||
args = { 'powershell', '-NoProfile', '-WindowStyle', 'Hidden', '-STA', '-ExecutionPolicy', 'Bypass', '-Command', ps },
|
||||
cancellable = false,
|
||||
})
|
||||
if res and res.status == 0 and res.stdout then
|
||||
@@ -807,8 +833,8 @@ local ensure_pipeline_helper_running
|
||||
local function _run_helper_request_response(req, timeout_seconds)
|
||||
_last_ipc_error = ''
|
||||
if not ensure_pipeline_helper_running() then
|
||||
_lua_log('ipc: helper not running; cannot execute request')
|
||||
_last_ipc_error = 'helper not running'
|
||||
_lua_log('ipc: helper not ready; cannot execute request')
|
||||
_last_ipc_error = 'helper not ready'
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -824,7 +850,6 @@ local function _run_helper_request_response(req, timeout_seconds)
|
||||
local rv = tostring(mp.get_property_native(PIPELINE_READY_PROP))
|
||||
_lua_log('ipc: helper not ready; ready=' .. rv)
|
||||
_last_ipc_error = 'helper not ready (ready=' .. rv .. ')'
|
||||
_pipeline_helper_started = false
|
||||
return nil
|
||||
end
|
||||
end
|
||||
@@ -875,7 +900,6 @@ local function _run_helper_request_response(req, timeout_seconds)
|
||||
|
||||
_lua_log('ipc: timeout waiting response; ' .. label)
|
||||
_last_ipc_error = 'timeout waiting response (' .. label .. ')'
|
||||
_pipeline_helper_started = false
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -893,56 +917,6 @@ local function _refresh_store_cache(timeout_seconds)
|
||||
local resp = _run_helper_request_response({ op = 'store-choices' }, timeout_seconds or 1)
|
||||
if not resp or not resp.success or type(resp.choices) ~= 'table' then
|
||||
_lua_log('stores: failed to load store choices via helper; stderr=' .. tostring(resp and resp.stderr or '') .. ' error=' .. tostring(resp and resp.error or ''))
|
||||
|
||||
-- Fallback: directly call Python to import MedeiaCLI.get_store_choices().
|
||||
-- This avoids helper IPC issues and still stays in sync with the REPL.
|
||||
local python = (opts and opts.python_path) and tostring(opts.python_path) or 'python'
|
||||
local cli_path = (opts and opts.cli_path) and tostring(opts.cli_path) or nil
|
||||
if not cli_path or cli_path == '' or not utils.file_info(cli_path) then
|
||||
local base_dir = mp.get_script_directory() or utils.getcwd() or ''
|
||||
if base_dir ~= '' then
|
||||
cli_path = find_file_upwards(base_dir, 'CLI.py', 8)
|
||||
end
|
||||
end
|
||||
|
||||
if cli_path and cli_path ~= '' then
|
||||
local root = tostring(cli_path):match('(.*)[/\\]') or ''
|
||||
if root ~= '' then
|
||||
local code = "import json, sys; sys.path.insert(0, r'" .. root .. "'); from CLI import MedeiaCLI; print(json.dumps(MedeiaCLI.get_store_choices()))"
|
||||
local res = utils.subprocess({
|
||||
args = { python, '-c', code },
|
||||
cancellable = false,
|
||||
})
|
||||
if res and res.status == 0 and res.stdout then
|
||||
local out_text = tostring(res.stdout)
|
||||
local last_line = ''
|
||||
for line in out_text:gmatch('[^\r\n]+') do
|
||||
if trim(line) ~= '' then
|
||||
last_line = line
|
||||
end
|
||||
end
|
||||
local ok, parsed = pcall(utils.parse_json, last_line ~= '' and last_line or out_text)
|
||||
if ok and type(parsed) == 'table' then
|
||||
local out = {}
|
||||
for _, v in ipairs(parsed) do
|
||||
local name = trim(tostring(v or ''))
|
||||
if name ~= '' then
|
||||
out[#out + 1] = name
|
||||
end
|
||||
end
|
||||
if #out > 0 then
|
||||
_cached_store_names = out
|
||||
_store_cache_loaded = true
|
||||
_lua_log('stores: loaded ' .. tostring(#_cached_store_names) .. ' stores via python fallback')
|
||||
return true
|
||||
end
|
||||
end
|
||||
else
|
||||
_lua_log('stores: python fallback failed; status=' .. tostring(res and res.status or 'nil') .. ' stderr=' .. tostring(res and res.stderr or ''))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -1295,11 +1269,8 @@ local function _run_pipeline_detached(pipeline_cmd)
|
||||
if not pipeline_cmd or pipeline_cmd == '' then
|
||||
return false
|
||||
end
|
||||
local python = (opts and opts.python_path) and tostring(opts.python_path) or 'python'
|
||||
local cli = (opts and opts.cli_path) and tostring(opts.cli_path) or 'CLI.py'
|
||||
local args = { python, cli, 'pipeline', '--pipeline', pipeline_cmd }
|
||||
local ok = utils.subprocess_detached({ args = args })
|
||||
return ok ~= nil
|
||||
local resp = _run_helper_request_response({ op = 'run-detached', data = { pipeline = pipeline_cmd } }, 1.0)
|
||||
return (resp and resp.success) and true or false
|
||||
end
|
||||
|
||||
local function _open_save_location_picker_for_pending_download()
|
||||
@@ -1659,62 +1630,10 @@ mp.register_script_message('medios-download-pick-path', function()
|
||||
end)
|
||||
|
||||
ensure_pipeline_helper_running = function()
|
||||
-- If a helper is already running (e.g. started by the launcher), just use it.
|
||||
if _is_pipeline_helper_ready() then
|
||||
_pipeline_helper_started = true
|
||||
return true
|
||||
end
|
||||
|
||||
-- We tried to start a helper before but it isn't ready anymore; restart.
|
||||
if _pipeline_helper_started then
|
||||
_pipeline_helper_started = false
|
||||
end
|
||||
|
||||
local helper_path = nil
|
||||
|
||||
-- Prefer deriving repo root from located CLI.py if available.
|
||||
if opts and opts.cli_path and utils.file_info(opts.cli_path) then
|
||||
local root = tostring(opts.cli_path):match('(.*)[/\\]') or ''
|
||||
if root ~= '' then
|
||||
local candidate = utils.join_path(root, 'MPV/pipeline_helper.py')
|
||||
if utils.file_info(candidate) then
|
||||
helper_path = candidate
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not helper_path then
|
||||
local base_dir = mp.get_script_directory() or ""
|
||||
if base_dir == "" then
|
||||
base_dir = utils.getcwd() or ""
|
||||
end
|
||||
helper_path = find_file_upwards(base_dir, 'MPV/pipeline_helper.py', 8)
|
||||
end
|
||||
if not helper_path then
|
||||
_lua_log('ipc: cannot find helper script MPV/pipeline_helper.py (script_dir=' .. tostring(mp.get_script_directory() or '') .. ')')
|
||||
return false
|
||||
end
|
||||
|
||||
-- Ensure mpv actually has a JSON IPC server for the helper to connect to.
|
||||
if not ensure_mpv_ipc_server() then
|
||||
_lua_log('ipc: mpv input-ipc-server is not set; start mpv with --input-ipc-server=\\\\.\\pipe\\mpv-medeia-macina')
|
||||
return false
|
||||
end
|
||||
|
||||
local python = (opts and opts.python_path) and tostring(opts.python_path) or 'python'
|
||||
local ipc = get_mpv_ipc_path()
|
||||
-- Give the helper enough time to connect (Windows pipe can take a moment).
|
||||
local args = {python, helper_path, '--ipc', ipc, '--timeout', '30'}
|
||||
_lua_log('ipc: starting helper: ' .. table.concat(args, ' '))
|
||||
|
||||
local ok = utils.subprocess_detached({ args = args })
|
||||
if ok == nil then
|
||||
_lua_log('ipc: failed to start helper (subprocess_detached returned nil)')
|
||||
return false
|
||||
end
|
||||
|
||||
_pipeline_helper_started = true
|
||||
return true
|
||||
-- IMPORTANT: do NOT spawn Python from inside mpv.
|
||||
-- The Python side (MPV.mpv_ipc) starts pipeline_helper.py using Windows
|
||||
-- no-console flags; spawning here can flash a console window.
|
||||
return _is_pipeline_helper_ready() and true or false
|
||||
end
|
||||
|
||||
local function run_pipeline_via_ipc(pipeline_cmd, seeds, timeout_seconds)
|
||||
@@ -1824,34 +1743,9 @@ function M.run_pipeline(pipeline_cmd, seeds)
|
||||
return nil
|
||||
end
|
||||
|
||||
local args = {opts.python_path, opts.cli_path, "pipeline", "--pipeline", pipeline_cmd}
|
||||
|
||||
if seeds then
|
||||
local seeds_json = utils.format_json(seeds)
|
||||
table.insert(args, "--seeds-json")
|
||||
table.insert(args, seeds_json)
|
||||
end
|
||||
|
||||
_lua_log("Running pipeline: " .. pipeline_cmd)
|
||||
-- If the persistent IPC helper isn't available, fall back to a subprocess.
|
||||
-- Note: mpv's subprocess helper does not support an `env` parameter.
|
||||
local res = utils.subprocess({
|
||||
args = args,
|
||||
cancellable = false,
|
||||
})
|
||||
|
||||
if res.status ~= 0 then
|
||||
local err = (res.stderr and res.stderr ~= "") and res.stderr
|
||||
or (res.error_string and res.error_string ~= "") and res.error_string
|
||||
or "unknown"
|
||||
local log_path = write_temp_log('medeia-cli-pipeline-stderr', tostring(res.stderr or err))
|
||||
local suffix = log_path and (' (log: ' .. log_path .. ')') or ''
|
||||
_lua_log("Pipeline error: " .. err .. suffix)
|
||||
mp.osd_message("Error: pipeline failed" .. suffix, 6)
|
||||
return nil
|
||||
end
|
||||
|
||||
return res.stdout
|
||||
mp.osd_message('Error: pipeline helper not available', 6)
|
||||
_lua_log('ipc: helper not available; refusing to spawn python subprocess')
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Helper to run pipeline and parse JSON output
|
||||
@@ -2132,13 +2026,12 @@ mp.add_key_binding("ctrl+del", "medios-delete", M.delete_current_file)
|
||||
mp.add_key_binding("l", "medeia-lyric-toggle", lyric_toggle)
|
||||
mp.add_key_binding("L", "medeia-lyric-toggle-shift", lyric_toggle)
|
||||
|
||||
-- Cover art observers removed (disabled per user request)
|
||||
|
||||
|
||||
-- Start the persistent pipeline helper eagerly at launch.
|
||||
-- This avoids spawning Python per command and works cross-platform via MPV IPC.
|
||||
mp.add_timeout(0, function()
|
||||
pcall(ensure_mpv_ipc_server)
|
||||
pcall(ensure_pipeline_helper_running)
|
||||
pcall(_lua_log, 'medeia-lua loaded version=' .. MEDEIA_LUA_VERSION)
|
||||
end)
|
||||
|
||||
|
||||
@@ -33,6 +33,29 @@ _LYRIC_LOG_FH: Optional[Any] = None
|
||||
_MPV_AVAILABILITY_CACHE: Optional[Tuple[bool, Optional[str]]] = None
|
||||
|
||||
|
||||
def _windows_pythonw_exe(python_exe: Optional[str]) -> Optional[str]:
|
||||
"""Return a pythonw.exe adjacent to python.exe if available (Windows only)."""
|
||||
if platform.system() != "Windows":
|
||||
return python_exe
|
||||
try:
|
||||
exe = str(python_exe or "").strip()
|
||||
except Exception:
|
||||
exe = ""
|
||||
if not exe:
|
||||
return None
|
||||
low = exe.lower()
|
||||
if low.endswith("pythonw.exe"):
|
||||
return exe
|
||||
if low.endswith("python.exe"):
|
||||
try:
|
||||
candidate = exe[:-10] + "pythonw.exe"
|
||||
if os.path.exists(candidate):
|
||||
return candidate
|
||||
except Exception:
|
||||
pass
|
||||
return exe
|
||||
|
||||
|
||||
def _windows_hidden_subprocess_kwargs() -> Dict[str, Any]:
|
||||
"""Best-effort kwargs to avoid flashing console windows on Windows.
|
||||
|
||||
@@ -413,8 +436,12 @@ class MPV:
|
||||
except Exception:
|
||||
repo_root = Path.cwd()
|
||||
|
||||
py = sys.executable
|
||||
if platform.system() == "Windows":
|
||||
py = _windows_pythonw_exe(py) or py
|
||||
|
||||
cmd: List[str] = [
|
||||
sys.executable,
|
||||
py or "python",
|
||||
"-m",
|
||||
"MPV.lyric",
|
||||
"--ipc",
|
||||
@@ -448,7 +475,18 @@ class MPV:
|
||||
# Make the current directory the repo root so `-m MPV.lyric` resolves reliably.
|
||||
kwargs["cwd"] = str(repo_root)
|
||||
if platform.system() == "Windows":
|
||||
kwargs["creationflags"] = 0x00000008 # DETACHED_PROCESS
|
||||
# Ensure we don't flash a console window when spawning the helper.
|
||||
flags = 0
|
||||
try:
|
||||
flags |= int(getattr(subprocess, "DETACHED_PROCESS", 0x00000008))
|
||||
except Exception:
|
||||
flags |= 0x00000008
|
||||
try:
|
||||
flags |= int(getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000))
|
||||
except Exception:
|
||||
flags |= 0x08000000
|
||||
kwargs["creationflags"] = flags
|
||||
kwargs.update({k: v for k, v in _windows_hidden_subprocess_kwargs().items() if k != "creationflags"})
|
||||
|
||||
_LYRIC_PROCESS = subprocess.Popen(cmd, **kwargs)
|
||||
debug(f"Lyric loader started (log={log_path})")
|
||||
@@ -582,6 +620,8 @@ class MPV:
|
||||
helper_path = (repo_root / "MPV" / "pipeline_helper.py").resolve()
|
||||
if helper_path.exists():
|
||||
py = sys.executable or "python"
|
||||
if platform.system() == "Windows":
|
||||
py = _windows_pythonw_exe(py) or py
|
||||
helper_cmd = [
|
||||
py,
|
||||
str(helper_path),
|
||||
@@ -591,6 +631,13 @@ class MPV:
|
||||
"30",
|
||||
]
|
||||
|
||||
helper_env = os.environ.copy()
|
||||
try:
|
||||
existing_pp = helper_env.get("PYTHONPATH")
|
||||
helper_env["PYTHONPATH"] = str(repo_root) if not existing_pp else (str(repo_root) + os.pathsep + str(existing_pp))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
helper_kwargs: Dict[str, Any] = {}
|
||||
if platform.system() == "Windows":
|
||||
flags = 0
|
||||
@@ -605,6 +652,9 @@ class MPV:
|
||||
helper_kwargs["creationflags"] = flags
|
||||
helper_kwargs.update({k: v for k, v in _windows_hidden_subprocess_kwargs().items() if k != "creationflags"})
|
||||
|
||||
helper_kwargs["cwd"] = str(repo_root)
|
||||
helper_kwargs["env"] = helper_env
|
||||
|
||||
subprocess.Popen(
|
||||
helper_cmd,
|
||||
stdin=subprocess.DEVNULL,
|
||||
|
||||
@@ -30,6 +30,8 @@ import time
|
||||
import logging
|
||||
import re
|
||||
import hashlib
|
||||
import subprocess
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
@@ -134,6 +136,91 @@ def _run_op(op: str, data: Any) -> Dict[str, Any]:
|
||||
"""
|
||||
op_name = str(op or "").strip().lower()
|
||||
|
||||
if op_name in {"run-detached", "run_detached", "pipeline-detached", "pipeline_detached"}:
|
||||
pipeline_text = ""
|
||||
seeds = None
|
||||
if isinstance(data, dict):
|
||||
pipeline_text = str(data.get("pipeline") or "").strip()
|
||||
seeds = data.get("seeds")
|
||||
if not pipeline_text:
|
||||
return {
|
||||
"success": False,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"error": "Missing pipeline",
|
||||
"table": None,
|
||||
}
|
||||
|
||||
py = sys.executable or "python"
|
||||
if platform.system() == "Windows":
|
||||
try:
|
||||
exe = str(py or "").strip()
|
||||
except Exception:
|
||||
exe = ""
|
||||
low = exe.lower()
|
||||
if low.endswith("python.exe"):
|
||||
try:
|
||||
candidate = exe[:-10] + "pythonw.exe"
|
||||
if os.path.exists(candidate):
|
||||
py = candidate
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cmd = [py, str((_repo_root() / "CLI.py").resolve()), "pipeline", "--pipeline", pipeline_text]
|
||||
if seeds is not None:
|
||||
try:
|
||||
cmd.extend(["--seeds-json", json.dumps(seeds, ensure_ascii=False)])
|
||||
except Exception:
|
||||
# Best-effort; seeds are optional.
|
||||
pass
|
||||
|
||||
popen_kwargs: Dict[str, Any] = {
|
||||
"stdin": subprocess.DEVNULL,
|
||||
"stdout": subprocess.DEVNULL,
|
||||
"stderr": subprocess.DEVNULL,
|
||||
"cwd": str(_repo_root()),
|
||||
}
|
||||
if platform.system() == "Windows":
|
||||
flags = 0
|
||||
try:
|
||||
flags |= int(getattr(subprocess, "DETACHED_PROCESS", 0x00000008))
|
||||
except Exception:
|
||||
flags |= 0x00000008
|
||||
try:
|
||||
flags |= int(getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000))
|
||||
except Exception:
|
||||
flags |= 0x08000000
|
||||
popen_kwargs["creationflags"] = int(flags)
|
||||
try:
|
||||
si = subprocess.STARTUPINFO()
|
||||
si.dwFlags |= int(getattr(subprocess, "STARTF_USESHOWWINDOW", 0x00000001))
|
||||
si.wShowWindow = subprocess.SW_HIDE
|
||||
popen_kwargs["startupinfo"] = si
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
popen_kwargs["start_new_session"] = True
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, **popen_kwargs)
|
||||
except Exception as exc:
|
||||
return {
|
||||
"success": False,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"error": f"Failed to spawn detached pipeline: {type(exc).__name__}: {exc}",
|
||||
"table": None,
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"error": None,
|
||||
"table": None,
|
||||
"pid": int(getattr(proc, "pid", 0) or 0),
|
||||
}
|
||||
|
||||
# Provide store backend choices using the same source as CLI/Typer autocomplete.
|
||||
if op_name in {"store-choices", "store_choices", "get-store-choices", "get_store_choices"}:
|
||||
from CLI import MedeiaCLI # noqa: WPS433
|
||||
|
||||
Reference in New Issue
Block a user