Skip to content

Commit e3b59bc

Browse files
committed
refine typehints in special/llm.py
* "Optional" can be replaced by "| None" in modern Pythons * "Tuple" can be replaced by lowercase "tuple" in modern Pythons * variable "cur" has a type of pymysql.cursors.Cursor * typehint contextlib variable "redirect" * change exit code to "int(e.code or 0)", a semantic change * split build_command_tree() using an inner _build_command_tree() to simplify the return type * don't pass mutable COMMAND_TREE as an argument default * typehint almost all function arguments * typehint all return types * remove a needless "return" statement * reformat many parameter lists as vertical
1 parent ff4ce79 commit e3b59bc

File tree

2 files changed

+33
-13
lines changed

2 files changed

+33
-13
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Internal
55
--------
66
* Add mypy to Pull Request template.
77
* Enable flake8-bugbear lint rules.
8+
* Improve typehints on LLM driver.
89

910

1011
1.40.0 (2025/10/14)

mycli/packages/special/llm.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
import shlex
88
import sys
99
from time import time
10-
from typing import Optional, Tuple
10+
from typing import Any
1111

1212
import click
1313
import llm
1414
from llm.cli import cli
15+
from pymysql.cursors import Cursor
1516

1617
from mycli.packages.special.main import Verbosity, parse_special_command
1718

@@ -22,15 +23,22 @@
2223
LLM_TEMPLATE_NAME = "mycli-llm-template"
2324

2425

25-
def run_external_cmd(cmd, *args, capture_output=False, restart_cli=False, raise_exception=True):
26+
def run_external_cmd(
27+
cmd: str,
28+
*args,
29+
capture_output=False,
30+
restart_cli=False,
31+
raise_exception=True,
32+
) -> tuple[int, str]:
2633
original_exe = sys.executable
2734
original_args = sys.argv
2835
try:
2936
sys.argv = [cmd] + list(args)
3037
code = 0
3138
if capture_output:
3239
buffer = io.StringIO()
33-
redirect = contextlib.ExitStack()
40+
redirect: contextlib.ExitStack[bool | None] | contextlib.nullcontext[None] = contextlib.ExitStack()
41+
assert isinstance(redirect, contextlib.ExitStack)
3442
redirect.enter_context(contextlib.redirect_stdout(buffer))
3543
redirect.enter_context(contextlib.redirect_stderr(buffer))
3644
else:
@@ -39,7 +47,7 @@ def run_external_cmd(cmd, *args, capture_output=False, restart_cli=False, raise_
3947
try:
4048
run_module(cmd, run_name="__main__")
4149
except SystemExit as e:
42-
code = e.code
50+
code = int(e.code or 0)
4351
if code != 0 and raise_exception:
4452
if capture_output:
4553
raise RuntimeError(buffer.getvalue())
@@ -62,24 +70,33 @@ def run_external_cmd(cmd, *args, capture_output=False, restart_cli=False, raise_
6270
sys.argv = original_args
6371

6472

65-
def build_command_tree(cmd):
66-
tree = {}
73+
def _build_command_tree(cmd) -> dict[str, Any] | None:
74+
tree: dict[str, Any] | None = {}
75+
assert isinstance(tree, dict)
6776
if isinstance(cmd, click.Group):
6877
for name, subcmd in cmd.commands.items():
6978
if cmd.name == "models" and name == "default":
7079
tree[name] = MODELS
7180
else:
72-
tree[name] = build_command_tree(subcmd)
81+
tree[name] = _build_command_tree(subcmd)
7382
else:
7483
tree = None
7584
return tree
7685

7786

87+
def build_command_tree(cmd) -> dict[str, Any]:
88+
return _build_command_tree(cmd) or {}
89+
90+
7891
# Generate the command tree for autocompletion
7992
COMMAND_TREE = build_command_tree(cli) if cli else {}
8093

8194

82-
def get_completions(tokens, tree=COMMAND_TREE):
95+
def get_completions(
96+
tokens: list[str],
97+
tree: dict[str, Any] | None = None,
98+
) -> list[str]:
99+
tree = tree or COMMAND_TREE
83100
for token in tokens:
84101
if token.startswith("-"):
85102
continue
@@ -150,16 +167,15 @@ def __init__(self, results=None):
150167
"""
151168

152169

153-
def ensure_mycli_template(replace=False):
170+
def ensure_mycli_template(replace: bool = False) -> None:
154171
if not replace:
155172
code, _ = run_external_cmd("llm", "templates", "show", LLM_TEMPLATE_NAME, capture_output=True, raise_exception=False)
156173
if code == 0:
157174
return
158175
run_external_cmd("llm", PROMPT, "--save", LLM_TEMPLATE_NAME)
159-
return
160176

161177

162-
def handle_llm(text, cur) -> Tuple[str, Optional[str], float]:
178+
def handle_llm(text: str, cur: Cursor) -> tuple[str, str | None, float]:
163179
_, verbosity, arg = parse_special_command(text)
164180
if not arg.strip():
165181
output = [(None, None, None, USAGE)]
@@ -214,12 +230,15 @@ def handle_llm(text, cur) -> Tuple[str, Optional[str], float]:
214230
raise RuntimeError(e)
215231

216232

217-
def is_llm_command(command) -> bool:
233+
def is_llm_command(command: str) -> bool:
218234
cmd, _, _ = parse_special_command(command)
219235
return cmd in ("\\llm", "\\ai")
220236

221237

222-
def sql_using_llm(cur, question=None) -> Tuple[str, Optional[str]]:
238+
def sql_using_llm(
239+
cur: Cursor | None,
240+
question: str | None = None,
241+
) -> tuple[str, str | None]:
223242
if cur is None:
224243
raise RuntimeError("Connect to a database and try again.")
225244
schema_query = """

0 commit comments

Comments
 (0)