feat(cli): add --version option 🤖🤖🤖 (#563)
* feat(cli): add version flag * fix(cli): parse args before acquiring single-instance lock so --version works while app is running Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
81bfe9345b
commit
eb16a34328
+13
-3
@@ -8,6 +8,8 @@ import atexit
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from .version import __version__
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
@@ -21,7 +23,13 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
def parse_arguments():
|
||||
"""Parse command line arguments."""
|
||||
parser = argparse.ArgumentParser(description="Vocalinux")
|
||||
parser = argparse.ArgumentParser(prog="vocalinux", description="Vocalinux")
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=f"%(prog)s {__version__}",
|
||||
help="Show the installed Vocalinux version and exit",
|
||||
)
|
||||
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
|
||||
# default model, language and engine are loaded from default config
|
||||
# due to priority of args over config
|
||||
@@ -214,6 +222,10 @@ def check_appindicator_support():
|
||||
|
||||
def main():
|
||||
"""Main entry point for the application."""
|
||||
# Parse arguments first so flags like --version work even when
|
||||
# another instance already holds the single-instance lock
|
||||
args = parse_arguments()
|
||||
|
||||
# Check for single instance BEFORE any initialization
|
||||
from . import single_instance
|
||||
|
||||
@@ -241,8 +253,6 @@ def main():
|
||||
# Register cleanup to release lock on exit
|
||||
atexit.register(single_instance.release_lock)
|
||||
|
||||
args = parse_arguments()
|
||||
|
||||
# Configure debug logging if requested
|
||||
if args.debug:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
@@ -8,6 +8,8 @@ Covers normal startup, debug mode, version flag, keyboard interrupt, and excepti
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from contextlib import redirect_stdout
|
||||
from io import StringIO
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
import pytest
|
||||
@@ -21,6 +23,14 @@ def _restore_sys_modules():
|
||||
added = set(sys.modules.keys()) - set(saved.keys())
|
||||
for k in added:
|
||||
del sys.modules[k]
|
||||
# Also drop the stale attribute from the parent package so that
|
||||
# mock.patch and a later re-import resolve to the same fresh module
|
||||
parent, _, child = k.rpartition(".")
|
||||
if parent and parent in sys.modules:
|
||||
try:
|
||||
delattr(sys.modules[parent], child)
|
||||
except AttributeError:
|
||||
pass
|
||||
for k, v in saved.items():
|
||||
if k not in sys.modules or sys.modules[k] is not v:
|
||||
sys.modules[k] = v
|
||||
@@ -50,6 +60,20 @@ class TestParseArguments(unittest.TestCase):
|
||||
args = parse_arguments()
|
||||
assert args.debug is True
|
||||
|
||||
def test_parse_args_version_flag(self):
|
||||
"""Test that --version prints the package version and exits."""
|
||||
from vocalinux.version import __version__
|
||||
|
||||
output = StringIO()
|
||||
with patch.object(sys, "argv", ["vocalinux", "--version"]):
|
||||
from vocalinux.main import parse_arguments
|
||||
|
||||
with redirect_stdout(output), self.assertRaises(SystemExit) as exit_context:
|
||||
parse_arguments()
|
||||
|
||||
assert exit_context.exception.code == 0
|
||||
assert output.getvalue() == f"vocalinux {__version__}\n"
|
||||
|
||||
def test_parse_args_model_argument(self):
|
||||
"""Test parsing with model argument."""
|
||||
with patch.object(sys, "argv", ["vocalinux", "--model", "small"]):
|
||||
@@ -282,6 +306,25 @@ class TestMainFunction(unittest.TestCase):
|
||||
main()
|
||||
assert exc_info.value.code == 1
|
||||
|
||||
def test_main_version_flag_with_lock_held(self):
|
||||
"""Test that --version works even when another instance holds the lock."""
|
||||
from vocalinux.main import main
|
||||
from vocalinux.version import __version__
|
||||
|
||||
mock_single_instance = MagicMock()
|
||||
mock_single_instance.acquire_lock.return_value = False
|
||||
|
||||
output = StringIO()
|
||||
with patch.dict(sys.modules, {"vocalinux.single_instance": mock_single_instance}):
|
||||
with patch.object(sys, "argv", ["vocalinux", "--version"]):
|
||||
with redirect_stdout(output):
|
||||
with pytest.raises(SystemExit) as exc_info:
|
||||
main()
|
||||
|
||||
assert exc_info.value.code == 0
|
||||
assert output.getvalue() == f"vocalinux {__version__}\n"
|
||||
mock_single_instance.acquire_lock.assert_not_called()
|
||||
|
||||
@patch("vocalinux.main.check_display_available")
|
||||
@patch("vocalinux.main.logging")
|
||||
@patch("vocalinux.main.check_dependencies")
|
||||
|
||||
Reference in New Issue
Block a user