Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Internal
* Move `--execute` logic to the new `main_modes` with `--batch`.
* Move `--list-dsn` logic to the new `main_modes` with `--batch`.
* Move `--list-ssh-config` logic to the new `main_modes` with `--batch`.
* Move REPL logic to the new `main_modes`.
* Move REPL logic to the new `main_modes`, and refactor the REPL.
* Sort coverage report in tox suite.
* Skip more tests when a database connection is not present.
* Move SQL utilities to a new `sql_utils.py`.
Expand Down
2 changes: 1 addition & 1 deletion mycli/clitoolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def get_toolbar_tokens() -> list[tuple[str, str]]:
result.append(("class:bottom-toolbar", "[F3] Multiline:"))
result.append(("class:bottom-toolbar.off", "OFF"))

if mycli.prompt_app.editing_mode == EditingMode.VI:
if mycli.prompt_session.editing_mode == EditingMode.VI:
result.append(divider)
result.append(("class:bottom-toolbar", "Vi:"))
result.append(("class:bottom-toolbar.on", _get_vi_mode()))
Expand Down
40 changes: 20 additions & 20 deletions mycli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def __init__(
self.defaults_suffix = defaults_suffix
self.login_path = login_path
self.toolbar_error_message: str | None = None
self.prompt_app: PromptSession | None = None
self.prompt_session: PromptSession | None = None
self._keepalive_counter = 0
self.keepalive_ticks: int | None = 0

Expand Down Expand Up @@ -291,7 +291,7 @@ def __init__(
self.terminal_window_title_format = c['main']['terminal_window_title']
self.multiplex_window_title_format = c['main']['multiplex_window_title']
self.multiplex_pane_title_format = c['main']['multiplex_pane_title']
self.prompt_app = None
self.prompt_session = None
self.destructive_keywords = [
keyword for keyword in c["main"].get("destructive_keywords", "DROP SHUTDOWN DELETE TRUNCATE ALTER UPDATE").split(' ') if keyword
]
Expand Down Expand Up @@ -904,8 +904,8 @@ def get_output_margin(self, status: str | None = None) -> int:
"""Get the output margin (number of rows for the prompt, footer and
timing message."""
if not self.prompt_lines:
if self.prompt_app and self.prompt_app.app:
render_counter = self.prompt_app.app.render_counter
if self.prompt_session and self.prompt_session.app:
render_counter = self.prompt_session.app.render_counter
else:
render_counter = 0
self.prompt_lines = self.get_prompt(self.prompt_format, render_counter).count('\n') + 1
Expand Down Expand Up @@ -933,8 +933,8 @@ def output(

"""
if output:
if self.prompt_app is not None:
size = self.prompt_app.output.get_size()
if self.prompt_session is not None:
size = self.prompt_session.output.get_size()
size_columns = size.columns
size_rows = size.rows
else:
Expand Down Expand Up @@ -1036,10 +1036,10 @@ def _on_completions_refreshed(self, new_completer: SQLCompleter) -> None:
with self._completer_lock:
self.completer = new_completer

if self.prompt_app:
if self.prompt_session:
# After refreshing, redraw the CLI to clear the statusbar
# "Refreshing completions..." indicator
self.prompt_app.app.invalidate()
self.prompt_session.app.invalidate()

def get_completions(self, text: str, cursor_position: int) -> Iterable[Completion]:
with self._completer_lock:
Expand All @@ -1054,22 +1054,22 @@ def set_all_external_titles(self) -> None:
def set_external_terminal_tab_title(self) -> None:
if not self.terminal_tab_title_format:
return
if not self.prompt_app:
if not self.prompt_session:
return
if not sys.stderr.isatty():
return
title = sanitize_terminal_title(self.get_prompt(self.terminal_tab_title_format, self.prompt_app.app.render_counter))
title = sanitize_terminal_title(self.get_prompt(self.terminal_tab_title_format, self.prompt_session.app.render_counter))
print(f'\x1b]1;{title}\a', file=sys.stderr, end='')
sys.stderr.flush()

def set_external_terminal_window_title(self) -> None:
if not self.terminal_window_title_format:
return
if not self.prompt_app:
if not self.prompt_session:
return
if not sys.stderr.isatty():
return
title = sanitize_terminal_title(self.get_prompt(self.terminal_window_title_format, self.prompt_app.app.render_counter))
title = sanitize_terminal_title(self.get_prompt(self.terminal_window_title_format, self.prompt_session.app.render_counter))
print(f'\x1b]2;{title}\a', file=sys.stderr, end='')
sys.stderr.flush()

Expand All @@ -1078,9 +1078,9 @@ def set_external_multiplex_window_title(self) -> None:
return
if not os.getenv('TMUX'):
return
if not self.prompt_app:
if not self.prompt_session:
return
title = sanitize_terminal_title(self.get_prompt(self.multiplex_window_title_format, self.prompt_app.app.render_counter))
title = sanitize_terminal_title(self.get_prompt(self.multiplex_window_title_format, self.prompt_session.app.render_counter))
try:
subprocess.run(
['tmux', 'rename-window', title],
Expand All @@ -1097,22 +1097,22 @@ def set_external_multiplex_pane_title(self) -> None:
return
if not os.getenv('TMUX'):
return
if not self.prompt_app:
if not self.prompt_session:
return
if not sys.stderr.isatty():
return
title = sanitize_terminal_title(self.get_prompt(self.multiplex_pane_title_format, self.prompt_app.app.render_counter))
title = sanitize_terminal_title(self.get_prompt(self.multiplex_pane_title_format, self.prompt_session.app.render_counter))
print(f'\x1b]2;{title}\x1b\\', file=sys.stderr, end='')
sys.stderr.flush()

def get_custom_toolbar(self, toolbar_format: str) -> ANSI:
if not self.prompt_app:
if not self.prompt_session:
return ANSI('')
if not self.prompt_app.app:
if not self.prompt_session.app:
return ANSI('')
if self.prompt_app.app.current_buffer.text:
if self.prompt_session.app.current_buffer.text:
return self.last_custom_toolbar_message
toolbar = self.get_prompt(toolbar_format, self.prompt_app.app.render_counter)
toolbar = self.get_prompt(toolbar_format, self.prompt_session.app.render_counter)
toolbar = toolbar.replace("\\x1b", "\x1b")
self.last_custom_toolbar_message = ANSI(toolbar)
return self.last_custom_toolbar_message
Expand Down
30 changes: 12 additions & 18 deletions mycli/main_modes/repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,6 @@
MIN_COMPLETION_TRIGGER = 1


def _main_module():
from mycli import main as main_module

return main_module


@dataclass(slots=True)
class ReplState:
iterations: int = 0
Expand Down Expand Up @@ -213,8 +207,8 @@ def _output_results(
break

if mycli.auto_vertical_output:
if mycli.prompt_app is not None:
max_width = mycli.prompt_app.output.get_size().columns
if mycli.prompt_session is not None:
max_width = mycli.prompt_session.output.get_size().columns
else:
max_width = DEFAULT_WIDTH
else:
Expand All @@ -239,8 +233,8 @@ def _output_results(
except KeyboardInterrupt:
pass
if mycli.beep_after_seconds > 0 and duration >= mycli.beep_after_seconds:
assert mycli.prompt_app is not None
mycli.prompt_app.output.bell()
assert mycli.prompt_session is not None
mycli.prompt_session.output.bell()
if special.is_timing_enabled():
mycli.output_timing(f'Time: {duration:0.03f}s')
except KeyboardInterrupt:
Expand Down Expand Up @@ -320,7 +314,7 @@ def _build_prompt_session(
else:
editing_mode = EditingMode.EMACS

mycli.prompt_app = PromptSession(
mycli.prompt_session = PromptSession(
color_depth=ColorDepth.DEPTH_24_BIT if 'truecolor' in os.getenv('COLORTERM', '').lower() else None,
lexer=PygmentsLexer(MyCliLexer),
reserve_space_for_menu=mycli.get_reserved_space(),
Expand Down Expand Up @@ -351,9 +345,9 @@ def _build_prompt_session(
)

if mycli.key_bindings == 'vi':
mycli.prompt_app.app.ttimeoutlen = mycli.vi_ttimeoutlen
mycli.prompt_session.app.ttimeoutlen = mycli.vi_ttimeoutlen
else:
mycli.prompt_app.app.ttimeoutlen = mycli.emacs_ttimeoutlen
mycli.prompt_session.app.ttimeoutlen = mycli.emacs_ttimeoutlen


def _one_iteration(
Expand All @@ -368,9 +362,9 @@ def _one_iteration(

if text is None:
try:
assert mycli.prompt_app is not None
loaded_message_fn = partial(_get_prompt_message, mycli, mycli.prompt_app.app)
text = mycli.prompt_app.prompt(
assert mycli.prompt_session is not None
loaded_message_fn = partial(_get_prompt_message, mycli, mycli.prompt_session.app)
text = mycli.prompt_session.prompt(
inputhook=inputhook,
message=loaded_message_fn,
)
Expand Down Expand Up @@ -420,8 +414,8 @@ def _one_iteration(
click.echo('---')
if special.is_timing_enabled():
mycli.output_timing(f'Time: {duration:.2f} seconds')
assert mycli.prompt_app is not None
text = mycli.prompt_app.prompt(
assert mycli.prompt_session is not None
text = mycli.prompt_session.prompt(
default=sql or '',
inputhook=inputhook,
message=loaded_message_fn,
Expand Down
4 changes: 2 additions & 2 deletions mycli/packages/key_binding_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ def handle_editor_command(
raise RuntimeError(message)
while True:
try:
assert isinstance(mycli.prompt_app, PromptSession)
text = mycli.prompt_app.prompt(
assert isinstance(mycli.prompt_session, PromptSession)
text = mycli.prompt_session.prompt(
default=sql,
inputhook=inputhook,
message=loaded_message_fn,
Expand Down
2 changes: 1 addition & 1 deletion test/pytests/test_clitoolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def make_mycli(
return SimpleNamespace(
completer=SimpleNamespace(smart_completion=smart_completion),
multi_line=multi_line,
prompt_app=SimpleNamespace(editing_mode=editing_mode),
prompt_session=SimpleNamespace(editing_mode=editing_mode),
toolbar_error_message=toolbar_error_message,
completion_refresher=SimpleNamespace(is_refreshing=MagicMock(return_value=refreshing)),
get_custom_toolbar=MagicMock(return_value="custom toolbar"),
Expand Down
12 changes: 6 additions & 6 deletions test/pytests/test_key_binding_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ class FakeMyCli:
def __init__(
self,
*,
prompt_app: FakePromptSession | None = None,
prompt_session: FakePromptSession | None = None,
last_query: str = 'last query',
) -> None:
self.prompt_app = prompt_app
self.prompt_session = prompt_session
self.last_query = last_query
self.toolbar_error_message: str | None = None

Expand Down Expand Up @@ -83,8 +83,8 @@ def test_handle_editor_command_returns_text_unchanged_when_not_editor_command(mo


def test_handle_editor_command_opens_editor_reprompts_after_keyboard_interrupt_and_returns_text(monkeypatch: pytest.MonkeyPatch) -> None:
prompt_app = FakePromptSession([KeyboardInterrupt(), 'edited sql'])
mycli = FakeMyCli(prompt_app=prompt_app)
prompt_session = FakePromptSession([KeyboardInterrupt(), 'edited sql'])
mycli = FakeMyCli(prompt_session=prompt_session)
open_calls: list[dict[str, str]] = []

def inputhook(*args: object, **kwargs: object) -> None:
Expand All @@ -111,14 +111,14 @@ def open_external_editor(*, filename: str | None, sql: str) -> tuple[str, str |

assert result == 'edited sql'
assert open_calls == [{'filename': 'query.sql', 'sql': 'last query'}]
assert prompt_app.prompt_calls == [
assert prompt_session.prompt_calls == [
{'default': 'SELECT 1', 'inputhook': inputhook, 'message': loaded_message_fn},
{'default': '', 'inputhook': inputhook, 'message': loaded_message_fn},
]


def test_handle_editor_command_uses_explicit_editor_query_and_raises_on_editor_error(monkeypatch: pytest.MonkeyPatch) -> None:
mycli = FakeMyCli(prompt_app=FakePromptSession([]))
mycli = FakeMyCli(prompt_session=FakePromptSession([]))

monkeypatch.setattr(key_binding_utils.special, 'editor_command', lambda text: True)
monkeypatch.setattr(key_binding_utils.special, 'get_filename', lambda text: 'query.sql')
Expand Down
4 changes: 2 additions & 2 deletions test/pytests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,11 +719,11 @@ class TestExecute:
def server_type(self):
return ["test"]

class PromptBuffer:
class TestPromptSession:
output = TestOutput()
app = None

m.prompt_app = PromptBuffer()
m.prompt_session = TestPromptSession()
m.sqlexecute = TestExecute()
m.explicit_pager = explicit_pager

Expand Down
Loading
Loading