Skip to content

Commit ecef22b

Browse files
author
Hamza
committed
Lazy-load CLI commands so Usage: augur [OPTIONS] COMMAND [ARGS]...
Augur is an application for open source community health analytics Options: --help Show this message and exit. Commands: api Commands for controlling the backend API server backend Commands for controlling the backend API server & data collection workers cache Commands for managing redis cache collection Commands for controlling the backend API server & data collection workers config Generate an augur.config.json csv_utils db Database utilities github Github utilities jumpstart tasks Commands for controlling the backend API server & data collection workers user Support for adding regular users or administrative users works without DB config Signed-off-by: Hamza <mezohafez1@gmail.com>
1 parent cfd638b commit ecef22b

1 file changed

Lines changed: 67 additions & 19 deletions

File tree

augur/application/cli/_multicommand.py

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,88 @@
1-
#SPDX-License-Identifier: MIT
1+
# SPDX-License-Identifier: MIT
22
"""
33
Runs Augur with Gunicorn when called
44
"""
55

6-
import os
7-
import click
8-
import importlib
9-
import traceback
6+
# pylint: disable=missing-class-docstring,missing-function-docstring,unused-argument
107

8+
import importlib
9+
import re
1110
from pathlib import Path
12-
# import augur.application
11+
from typing import Optional
12+
13+
import click
14+
15+
CONTEXT_SETTINGS = {"auto_envvar_prefix": "AUGUR"}
16+
17+
18+
def _extract_short_help(py_file: Path) -> Optional[str]:
19+
"""
20+
Best-effort parse short_help from the command module without importing it.
21+
Looks for short_help='...' or short_help="..."
22+
"""
23+
try:
24+
text = py_file.read_text(encoding="utf-8", errors="ignore")
25+
except (OSError, UnicodeError):
26+
return None
27+
28+
29+
m = re.search(r"short_help\s*=\s*(['\"])(.*?)\1", text)
30+
if not m:
31+
return None
32+
return m.group(2).strip() or None
33+
34+
35+
class LazyAugurCommand(click.Command):
36+
"""
37+
A lightweight proxy command that imports the real command only when invoked.
38+
This prevents eager imports during `augur --help`.
39+
"""
40+
41+
def __init__(self, name: str, import_name: str, short_help: Optional[str]):
42+
super().__init__(name=name, callback=self._proxy_callback, short_help=short_help)
43+
self._import_name = import_name
44+
self._real_cmd = None
45+
46+
def _load(self) -> click.Command:
47+
if self._real_cmd is None:
48+
module = importlib.import_module(self._import_name)
49+
self._real_cmd = module.cli
50+
return self._real_cmd
51+
52+
def _proxy_callback(self, *args, **kwargs):
53+
# If click ends up calling callback directly, delegate
54+
return self._load().callback(*args, **kwargs)
55+
56+
def invoke(self, ctx):
57+
return self._load().invoke(ctx)
58+
59+
def get_params(self, ctx):
60+
return self._load().get_params(ctx)
61+
62+
def get_help(self, ctx):
63+
return self._load().get_help(ctx)
1364

14-
CONTEXT_SETTINGS = dict(auto_envvar_prefix='AUGUR')
1565

1666
class AugurMultiCommand(click.MultiCommand):
17-
def __commands_folder(self):
18-
return os.path.abspath(os.path.dirname(__file__))
67+
def __commands_folder(self) -> Path:
68+
return Path(__file__).resolve().parent
1969

2070
def list_commands(self, ctx):
2171
rv = []
22-
for filename in os.listdir(self.__commands_folder()):
23-
if not filename.startswith('_') and filename.endswith('.py'):
24-
rv.append(filename[:-3])
72+
for p in self.__commands_folder().iterdir():
73+
if p.is_file() and p.suffix == ".py" and not p.name.startswith("_"):
74+
rv.append(p.stem)
2575
rv.sort()
2676
return rv
2777

2878
def get_command(self, ctx, name):
29-
cmdfile = "augur/application/cli" / Path(name + ".py")
30-
31-
# Check that the command exists before importing
79+
cmdfile = self.__commands_folder() / f"{name}.py"
3280
if not cmdfile.is_file():
33-
return
81+
return None
82+
short_help = _extract_short_help(cmdfile)
83+
import_name = f"augur.application.cli.{name}"
84+
return LazyAugurCommand(name=name, import_name=import_name, short_help=short_help)
3485

35-
# Prefer to raise exception instead of silcencing it
36-
module = importlib.import_module('.' + name, 'augur.application.cli')
37-
return module.cli
3886

3987
@click.command(cls=AugurMultiCommand, context_settings=CONTEXT_SETTINGS)
4088
@click.pass_context

0 commit comments

Comments
 (0)