feat: corrigida a resolução do Node.js ao executar planos Python p
- corrigida a resolução do Node.js ao executar planos Python pelo painel CEP Resumo: - 2 arquivos alterados - 1 novos - 1 modificados - 0 removidos 1 file changed, 38 insertions(+), 1 deletion(-) Arquivos: - code/engine/aplicar_plano_de_edicao.py - code/engine/testes/test_aplicar_plano_de_edicao.py
This commit is contained in:
@@ -18,6 +18,8 @@ troca de sequência.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -41,6 +43,40 @@ from engine.integracoes.premiere.sessao_mcp import SessaoMCP
|
|||||||
CAMINHO_DO_SERVIDOR_MCP = CAMINHO_DO_CODIGO / "dist" / "index.js"
|
CAMINHO_DO_SERVIDOR_MCP = CAMINHO_DO_CODIGO / "dist" / "index.js"
|
||||||
|
|
||||||
|
|
||||||
|
def resolver_caminho_do_node() -> str:
|
||||||
|
"""Resolve o executável Node mesmo quando o painel CEP possui um PATH reduzido.
|
||||||
|
|
||||||
|
A variável ``PREMIERE_NODE_BIN`` permite que o painel ou o sistema informe
|
||||||
|
um caminho explícito. Em seguida, a resolução tenta o ``PATH`` e os
|
||||||
|
caminhos comuns de instalação local. Levanta :class:`FileNotFoundError`
|
||||||
|
com orientação acionável quando o Node não está disponível.
|
||||||
|
"""
|
||||||
|
caminho_configurado = os.environ.get("PREMIERE_NODE_BIN", "").strip()
|
||||||
|
if caminho_configurado:
|
||||||
|
if os.path.isfile(caminho_configurado) and os.access(caminho_configurado, os.X_OK):
|
||||||
|
return caminho_configurado
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"Node.js configurado em PREMIERE_NODE_BIN não é executável: {caminho_configurado}"
|
||||||
|
)
|
||||||
|
|
||||||
|
encontrado_no_path = shutil.which("node.exe" if os.name == "nt" else "node")
|
||||||
|
if encontrado_no_path:
|
||||||
|
return encontrado_no_path
|
||||||
|
|
||||||
|
caminhos_conhecidos = (
|
||||||
|
("C:/Program Files/nodejs/node.exe", "C:/Program Files/nodejs/nodejs.exe")
|
||||||
|
if os.name == "nt"
|
||||||
|
else ("/opt/homebrew/bin/node", "/usr/local/bin/node", "/usr/bin/node")
|
||||||
|
)
|
||||||
|
for caminho in caminhos_conhecidos:
|
||||||
|
if os.path.isfile(caminho) and os.access(caminho, os.X_OK):
|
||||||
|
return caminho
|
||||||
|
raise FileNotFoundError(
|
||||||
|
"Node.js não encontrado. Instale o Node.js ou configure PREMIERE_NODE_BIN "
|
||||||
|
"com o caminho completo do executável."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def executar(caminho_do_plano: Path) -> dict:
|
def executar(caminho_do_plano: Path) -> dict:
|
||||||
"""Lê o plano em ``caminho_do_plano`` e o aplica na sequência ativa do Premiere.
|
"""Lê o plano em ``caminho_do_plano`` e o aplica na sequência ativa do Premiere.
|
||||||
|
|
||||||
@@ -52,7 +88,8 @@ def executar(caminho_do_plano: Path) -> dict:
|
|||||||
# A pausa é aplicada no cliente, e não dentro do aplicador, para valer
|
# A pausa é aplicada no cliente, e não dentro do aplicador, para valer
|
||||||
# igualmente nas leituras da timeline e nas escritas — são todas
|
# igualmente nas leituras da timeline e nas escritas — são todas
|
||||||
# round-trips síncronos no processo principal do Premiere.
|
# round-trips síncronos no processo principal do Premiere.
|
||||||
cliente = ClienteMCPComPausa(ClienteMCPPorStdio(["node", str(CAMINHO_DO_SERVIDOR_MCP)]))
|
caminho_do_node = resolver_caminho_do_node()
|
||||||
|
cliente = ClienteMCPComPausa(ClienteMCPPorStdio([caminho_do_node, str(CAMINHO_DO_SERVIDOR_MCP)]))
|
||||||
with SessaoMCP(cliente):
|
with SessaoMCP(cliente):
|
||||||
acesso_ao_editor = AcessoAoEditor(AcessoATimeline(cliente))
|
acesso_ao_editor = AcessoAoEditor(AcessoATimeline(cliente))
|
||||||
conversor_de_timeline = ConversorDeTimeline()
|
conversor_de_timeline = ConversorDeTimeline()
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
"""Testes da resolução do executável Node usada pelo aplicador de planos."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from engine import aplicar_plano_de_edicao
|
||||||
|
|
||||||
|
|
||||||
|
class TesteResolucaoDoNode(unittest.TestCase):
|
||||||
|
"""Garante que a engine não dependa do PATH herdado pelo painel CEP."""
|
||||||
|
|
||||||
|
def test_prioriza_executavel_configurado_no_ambiente(self):
|
||||||
|
"""Usa o caminho explícito quando o painel ou sistema o fornece."""
|
||||||
|
with patch.dict(os.environ, {"PREMIERE_NODE_BIN": "/custom/node"}, clear=False), patch(
|
||||||
|
"engine.aplicar_plano_de_edicao.os.path.isfile", return_value=True
|
||||||
|
), patch("engine.aplicar_plano_de_edicao.os.access", return_value=True):
|
||||||
|
self.assertEqual(aplicar_plano_de_edicao.resolver_caminho_do_node(), "/custom/node")
|
||||||
|
|
||||||
|
def test_usa_caminho_conhecido_quando_node_nao_esta_no_path(self):
|
||||||
|
"""Encontra a instalação Homebrew mesmo com PATH incompleto."""
|
||||||
|
with patch.dict(os.environ, {}, clear=True), patch(
|
||||||
|
"engine.aplicar_plano_de_edicao.shutil.which", return_value=None
|
||||||
|
), patch("engine.aplicar_plano_de_edicao.os.path.isfile", side_effect=lambda caminho: caminho == "/opt/homebrew/bin/node"), patch(
|
||||||
|
"engine.aplicar_plano_de_edicao.os.access", return_value=True
|
||||||
|
):
|
||||||
|
self.assertEqual(aplicar_plano_de_edicao.resolver_caminho_do_node(), "/opt/homebrew/bin/node")
|
||||||
|
|
||||||
|
def test_informa_instalacao_ausente(self):
|
||||||
|
"""Levanta um erro explicativo quando nenhum executável existe."""
|
||||||
|
with patch.dict(os.environ, {}, clear=True), patch(
|
||||||
|
"engine.aplicar_plano_de_edicao.shutil.which", return_value=None
|
||||||
|
), patch("engine.aplicar_plano_de_edicao.os.path.isfile", return_value=False):
|
||||||
|
with self.assertRaisesRegex(FileNotFoundError, "Node.js não encontrado"):
|
||||||
|
aplicar_plano_de_edicao.resolver_caminho_do_node()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user