lithe-cli 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/PKG-INFO +1 -1
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli/__init__.py +1 -1
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli/main.py +9 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli/setup.py +12 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli.egg-info/PKG-INFO +1 -1
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/tests/test_cli.py +40 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/LICENSE +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/README.md +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli/__main__.py +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli/agent.py +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli/config.py +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli/py.typed +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli/ui.py +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli.egg-info/SOURCES.txt +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli.egg-info/dependency_links.txt +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli.egg-info/entry_points.txt +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli.egg-info/requires.txt +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/lithe_cli.egg-info/top_level.txt +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/pyproject.toml +0 -0
- {lithe_cli-0.2.2 → lithe_cli-0.2.3}/setup.cfg +0 -0
|
@@ -336,6 +336,15 @@ def _chat_loop(cfg: Any) -> int:
|
|
|
336
336
|
except (EOFError, KeyboardInterrupt):
|
|
337
337
|
print()
|
|
338
338
|
break
|
|
339
|
+
except UnicodeDecodeError:
|
|
340
|
+
print(
|
|
341
|
+
ui.s(
|
|
342
|
+
"\n(这行输入无法按 UTF-8 解码,已丢弃;"
|
|
343
|
+
"请把终端与 locale 设为 UTF-8(如 export LANG=C.UTF-8)后重试。)",
|
|
344
|
+
RED,
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
continue
|
|
339
348
|
if not line:
|
|
340
349
|
continue
|
|
341
350
|
if line in ("/exit", "/quit", "exit", "quit"):
|
|
@@ -156,6 +156,15 @@ def _prompt(
|
|
|
156
156
|
except (EOFError, KeyboardInterrupt):
|
|
157
157
|
print()
|
|
158
158
|
return None
|
|
159
|
+
except UnicodeDecodeError:
|
|
160
|
+
print(
|
|
161
|
+
ui.s(
|
|
162
|
+
"\n 这行输入无法按 UTF-8 解码,已丢弃;"
|
|
163
|
+
"请把终端与 locale 设为 UTF-8(如 export LANG=C.UTF-8)后重试。",
|
|
164
|
+
RED,
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
continue
|
|
159
168
|
if not answer and default:
|
|
160
169
|
answer = default
|
|
161
170
|
if not answer:
|
|
@@ -177,6 +186,9 @@ def _yes_no(label: str, default: bool, input_fn=input) -> bool:
|
|
|
177
186
|
except (EOFError, KeyboardInterrupt):
|
|
178
187
|
print()
|
|
179
188
|
return default
|
|
189
|
+
except UnicodeDecodeError:
|
|
190
|
+
print(ui.s("\n 这行输入无法按 UTF-8 解码,按默认值处理。", RED))
|
|
191
|
+
return default
|
|
180
192
|
return default if not answer else answer in ("y", "yes", "是")
|
|
181
193
|
|
|
182
194
|
|
|
@@ -444,6 +444,46 @@ def test_read_masked_echoes_stars_and_swallows_paste_markers():
|
|
|
444
444
|
assert "\x1b[200~" not in text and "\x1b[201~" not in text
|
|
445
445
|
|
|
446
446
|
|
|
447
|
+
def test_undecodable_input_line_never_crashes(tmp_path, monkeypatch, capsys):
|
|
448
|
+
# a mangled (non-UTF-8) input line — e.g. readline/locale mismatch —
|
|
449
|
+
# must be dropped with a hint, not kill the whole process
|
|
450
|
+
def bad_decode(prompt=""):
|
|
451
|
+
raise UnicodeDecodeError("utf-8", b"\xe5", 52, 53, "invalid continuation byte")
|
|
452
|
+
|
|
453
|
+
from lithe_cli import setup
|
|
454
|
+
from lithe_cli.main import _chat_loop
|
|
455
|
+
from conftest import make_config
|
|
456
|
+
|
|
457
|
+
calls = iter([bad_decode, EOFError])
|
|
458
|
+
|
|
459
|
+
def fake_input(prompt=""):
|
|
460
|
+
f = next(calls)
|
|
461
|
+
if isinstance(f, type(EOFError)):
|
|
462
|
+
raise f
|
|
463
|
+
f(prompt)
|
|
464
|
+
|
|
465
|
+
monkeypatch.setattr("builtins.input", fake_input)
|
|
466
|
+
assert _chat_loop(make_config(tmp_path, [])) == 0
|
|
467
|
+
out = capsys.readouterr().out
|
|
468
|
+
assert "UTF-8" in out
|
|
469
|
+
|
|
470
|
+
# the wizard prompts take the same beating and re-ask instead of dying
|
|
471
|
+
answers = iter([bad_decode, "https://ok.example/api"])
|
|
472
|
+
|
|
473
|
+
def wizard_input(prompt=""):
|
|
474
|
+
f = next(answers)
|
|
475
|
+
if callable(f):
|
|
476
|
+
f(prompt)
|
|
477
|
+
return ""
|
|
478
|
+
return f
|
|
479
|
+
|
|
480
|
+
assert (
|
|
481
|
+
setup._prompt("Base URL", validate=setup._http_like, input_fn=wizard_input)
|
|
482
|
+
== "https://ok.example/api"
|
|
483
|
+
)
|
|
484
|
+
assert "UTF-8" in capsys.readouterr().out
|
|
485
|
+
|
|
486
|
+
|
|
447
487
|
def test_wizard_rejects_bad_url_then_accepts(tmp_path, monkeypatch, capsys):
|
|
448
488
|
from lithe_cli import setup
|
|
449
489
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|