Coverage for src/pip_project_template/cli/_GlobalArgumentParser.py: 100.00%
38 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-08-28 08:53 +1000
« prev ^ index » next coverage.py v7.9.2, created at 2025-08-28 08:53 +1000
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Timestamp: "2025-08-27 10:45:47 (ywatanabe)"
4# File: /home/ywatanabe/proj/pip-project-template/src/pip_project_template/cli/_GlobalArgumentParser.py
5# ----------------------------------------
6from __future__ import annotations
7import os
8__FILE__ = (
9 "./src/pip_project_template/cli/_GlobalArgumentParser.py"
10)
11__DIR__ = os.path.dirname(__FILE__)
12# ----------------------------------------
14"""Central argument parser configuration."""
16import argparse
17import importlib
18import pkgutil
19from typing import Dict, Tuple
22class GlobalArgumentParser:
23 """Central argument parser for all CLI commands."""
25 @classmethod
26 def get_command_parsers(cls):
27 """Dynamically discover and load parsers from command modules."""
28 parsers = {}
29 descriptions = {}
31 # Get the directory of this CLI package
32 cli_package_path = os.path.dirname(__file__)
34 # Scan all Python files in the CLI directory
35 for importer, modname, ispkg in pkgutil.iter_modules(
36 [cli_package_path]
37 ):
38 # Skip private modules and the central parser itself
39 if modname.startswith("_") or modname == "__pycache__":
40 continue
42 try:
43 # Dynamic import
44 module = importlib.import_module(
45 f".{modname}", package="pip_project_template.cli"
46 )
48 # Check if module has create_parser function
49 if hasattr(module, "create_parser"):
50 parser = module.create_parser()
51 command_name = modname.replace(
52 "_", "-"
53 ) # Convert underscores to hyphens for CLI
54 parsers[command_name] = parser
55 descriptions[command_name] = getattr(
56 parser, "description", f"Run {command_name} command"
57 )
59 except (
60 ImportError,
61 AttributeError,
62 Exception,
63 ) as e:
64 # Silently skip modules that can't be imported or don't have create_parser
65 continue
67 return parsers, descriptions
69 @classmethod
70 def get_main_parser(cls) -> Tuple[argparse.ArgumentParser, Dict]:
71 """Create main parser with subcommands."""
72 parser = argparse.ArgumentParser(
73 prog="python -m pip_project_template",
74 description="Pip Project Template CLI",
75 )
77 subparsers = parser.add_subparsers(
78 dest="command", help="Available commands"
79 )
81 parsers, descriptions = cls.get_command_parsers()
82 subparsers_dict = {}
84 for command_name, command_parser in parsers.items():
85 # Create subparser with description from individual parser
86 subparser = subparsers.add_parser(
87 command_name,
88 help=descriptions.get(
89 command_name, f"Run {command_name} command"
90 ),
91 parents=[command_parser],
92 add_help=False,
93 )
94 subparsers_dict[command_name] = subparser
96 return parser, subparsers_dict
98# EOF