fix(config): open nano on the terminal in config edit

pkgmgr config edit ran nano through run_command, which pipes stdout and stderr to capture them, so nano aborted with 'Standard output is not a terminal'. The editor now runs with subprocess.call and the inherited terminal, and a failing editor exits with its code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-10-29 13:47:45 +01:00
1 parent c7b070a62b
commit eb17cddd21
2 files changed
+39 -2

No files matched your search

+4 -2
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import os import os
import shutil import shutil
import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -14,7 +15,6 @@ from pkgmgr.actions.config.add import interactive_add
from pkgmgr.actions.config.init import config_init from pkgmgr.actions.config.init import config_init
from pkgmgr.actions.config.show import show_config from pkgmgr.actions.config.show import show_config
from pkgmgr.cli.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.core.command.run import run_command
from pkgmgr.core.config.save import save_user_config from pkgmgr.core.config.save import save_user_config
from pkgmgr.core.repository.resolve import resolve_repos from pkgmgr.core.repository.resolve import resolve_repos
@@ -108,7 +108,9 @@ def handle_config(args, ctx: CLIContext) -> None:
return return
if args.subcommand == "edit": if args.subcommand == "edit":
run_command(f"nano {user_config_path}") returncode = subprocess.call(["nano", os.path.expanduser(user_config_path)])
if returncode != 0:
sys.exit(returncode)
return return
if args.subcommand == "init": if args.subcommand == "init":
@@ -0,0 +1,35 @@
from __future__ import annotations
import types
import unittest
from unittest.mock import patch
from pkgmgr.cli.commands import config as config_cmd
class ConfigEditTests(unittest.TestCase):
def _edit(self, path: str, returncode: int):
with patch(
"subprocess.Popen",
side_effect=AssertionError("the editor's output must not be captured"),
), patch("subprocess.call", return_value=returncode) as call:
config_cmd.handle_config(
types.SimpleNamespace(subcommand="edit"),
types.SimpleNamespace(user_config_path=path),
)
return call
def test_nano_inherits_the_terminal(self):
call = self._edit("/cfg/config.yaml", 0)
call.assert_called_once_with(["nano", "/cfg/config.yaml"])
def test_a_failing_editor_exits_with_its_code(self):
with self.assertRaises(SystemExit) as raised:
self._edit("/cfg/config.yaml", 3)
self.assertEqual(raised.exception.code, 3)
if __name__ == "__main__":
unittest.main()