Skip to content

Commit 4ddffce

Browse files
authored
Merge pull request #199 from dbcli/fix-prompt
Fix windows path shown in prompt.
2 parents 9434b24 + 2965476 commit 4ddffce

3 files changed

Lines changed: 113 additions & 36 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
- This adds a new `\llm` special command
77
- eg: `\llm "Who is the largest customer based on revenue?"`
88

9+
### Bug Fixes
10+
11+
* Fix the windows path shown in prompt to remove escaping.
12+
913
### Internal
1014

1115
* Change min required python version to 3.9+

litecli/main.py

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,50 @@
1-
from __future__ import unicode_literals
2-
from __future__ import print_function
1+
from __future__ import print_function, unicode_literals
32

3+
import itertools
4+
import logging
45
import os
6+
import re
7+
import shutil
58
import sys
6-
import traceback
7-
import logging
89
import threading
9-
from time import time
10+
import traceback
11+
from collections import namedtuple
1012
from datetime import datetime
1113
from io import open
12-
from collections import namedtuple
1314
from sqlite3 import OperationalError, sqlite_version
14-
import shutil
15+
from time import time
1516

16-
from cli_helpers.tabular_output import TabularOutputFormatter
17-
from cli_helpers.tabular_output import preprocessors
1817
import click
1918
import sqlparse
19+
from cli_helpers.tabular_output import TabularOutputFormatter, preprocessors
20+
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
2021
from prompt_toolkit.completion import DynamicCompleter
21-
from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
22-
from prompt_toolkit.shortcuts import PromptSession, CompleteStyle
2322
from prompt_toolkit.document import Document
23+
from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
2424
from prompt_toolkit.filters import HasFocus, IsDone
2525
from prompt_toolkit.formatted_text import ANSI
26+
from prompt_toolkit.history import FileHistory
2627
from prompt_toolkit.layout.processors import (
27-
HighlightMatchingBracketProcessor,
2828
ConditionalProcessor,
29+
HighlightMatchingBracketProcessor,
2930
)
3031
from prompt_toolkit.lexers import PygmentsLexer
31-
from prompt_toolkit.history import FileHistory
32-
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
32+
from prompt_toolkit.shortcuts import CompleteStyle, PromptSession
3333

34-
from .packages.special.main import NO_QUERY
35-
from .packages.prompt_utils import confirm, confirm_destructive_query
36-
from .packages import special
37-
from .sqlcompleter import SQLCompleter
38-
from .clitoolbar import create_toolbar_tokens_func
39-
from .clistyle import style_factory, style_factory_output
40-
from .sqlexecute import SQLExecute
34+
from .__init__ import __version__
4135
from .clibuffer import cli_is_multiline
36+
from .clistyle import style_factory, style_factory_output
37+
from .clitoolbar import create_toolbar_tokens_func
4238
from .completion_refresher import CompletionRefresher
4339
from .config import config_location, ensure_dir_exists, get_config
4440
from .key_bindings import cli_bindings
4541
from .lexer import LiteCliLexer
46-
from .__init__ import __version__
42+
from .packages import special
4743
from .packages.filepaths import dir_path_exists
48-
49-
import itertools
44+
from .packages.prompt_utils import confirm, confirm_destructive_query
45+
from .packages.special.main import NO_QUERY
46+
from .sqlcompleter import SQLCompleter
47+
from .sqlexecute import SQLExecute
5048

5149
click.disable_unicode_literals_warning = True
5250

@@ -758,20 +756,32 @@ def get_completions(self, text, cursor_positition):
758756
return self.completer.get_completions(Document(text=text, cursor_position=cursor_positition), None)
759757

760758
def get_prompt(self, string):
761-
self.logger.debug("Getting prompt")
759+
self.logger.debug("Getting prompt %r", string)
762760
sqlexecute = self.sqlexecute
763761
now = datetime.now()
764-
string = string.replace("\\d", sqlexecute.dbname or "(none)")
765-
string = string.replace("\\f", os.path.basename(sqlexecute.dbname or "(none)"))
766-
string = string.replace("\\n", "\n")
767-
string = string.replace("\\D", now.strftime("%a %b %d %H:%M:%S %Y"))
768-
string = string.replace("\\m", now.strftime("%M"))
769-
string = string.replace("\\P", now.strftime("%p"))
770-
string = string.replace("\\R", now.strftime("%H"))
771-
string = string.replace("\\r", now.strftime("%I"))
772-
string = string.replace("\\s", now.strftime("%S"))
773-
string = string.replace("\\_", " ")
774-
return string
762+
763+
# Prepare the replacements dictionary
764+
replacements = {
765+
r"\d": sqlexecute.dbname or "(none)",
766+
r"\f": os.path.basename(sqlexecute.dbname or "(none)"),
767+
r"\n": "\n",
768+
r"\D": now.strftime("%a %b %d %H:%M:%S %Y"),
769+
r"\m": now.strftime("%M"),
770+
r"\P": now.strftime("%p"),
771+
r"\R": now.strftime("%H"),
772+
r"\r": now.strftime("%I"),
773+
r"\s": now.strftime("%S"),
774+
r"\_": " ",
775+
}
776+
# Compile a regex pattern that matches any of the keys in replacements
777+
pattern = re.compile("|".join(re.escape(key) for key in replacements.keys()))
778+
779+
# Define the replacement function
780+
def replacer(match):
781+
return replacements[match.group(0)]
782+
783+
# Perform the substitution
784+
return pattern.sub(replacer, string)
775785

776786
def run_query(self, query, new_line=True):
777787
"""Runs *query*."""

tests/test_main.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from collections import namedtuple
33
from textwrap import dedent
44
import shutil
5+
from datetime import datetime
6+
from unittest.mock import patch
57

68
import click
79
from click.testing import CliRunner
@@ -267,3 +269,64 @@ def test_startup_commands(executor):
267269
]
268270

269271
# implement tests on executions of the startupcommands
272+
273+
274+
@patch("litecli.main.datetime") # Adjust if your module path is different
275+
def test_get_prompt(mock_datetime):
276+
# We'll freeze time at 2025-01-20 13:37:42 for comedic effect.
277+
# Because "leet" times call for 13:37!
278+
frozen_time = datetime(2025, 1, 20, 13, 37, 42)
279+
mock_datetime.now.return_value = frozen_time
280+
# Ensure `datetime` class is still accessible for strftime usage
281+
mock_datetime.datetime = datetime
282+
283+
# Instantiate and connect
284+
lc = LiteCli()
285+
lc.connect("/tmp/litecli_test.db")
286+
287+
# 1. Test \d => full path to the DB
288+
assert lc.get_prompt(r"\d") == "/tmp/litecli_test.db"
289+
290+
# 2. Test \f => basename of the DB
291+
# (because "f" stands for "filename", presumably!)
292+
assert lc.get_prompt(r"\f") == "litecli_test.db"
293+
294+
# 3. Test \_ => single space
295+
assert lc.get_prompt(r"Hello\_World") == "Hello World"
296+
297+
# 4. Test \n => newline
298+
# Just to be sure we're only inserting a newline,
299+
# we can check length or assert the presence of "\n".
300+
expected = f"Line1{os.linesep}Line2"
301+
assert lc.get_prompt(r"Line1\nLine2") == expected
302+
303+
# 5. Test date/time placeholders (with frozen time):
304+
# \D => e.g. 'Mon Jan 20 13:37:42 2025'
305+
expected_date_str = frozen_time.strftime("%a %b %d %H:%M:%S %Y")
306+
assert lc.get_prompt(r"\D") == expected_date_str
307+
308+
# 6. Test \m => minutes
309+
assert lc.get_prompt(r"\m") == "37"
310+
311+
# 7. Test \P => AM/PM
312+
# 13:37 is PM
313+
assert lc.get_prompt(r"\P") == "PM"
314+
315+
# 8. Test \R => 24-hour format hour
316+
assert lc.get_prompt(r"\R") == "13"
317+
318+
# 9. Test \r => 12-hour format hour
319+
# 13:37 is 01 in 12-hour format
320+
assert lc.get_prompt(r"\r") == "01"
321+
322+
# 10. Test \s => seconds
323+
assert lc.get_prompt(r"\s") == "42"
324+
325+
# 11. Test when dbname is None => (none)
326+
lc.connect(None) # Simulate no DB connection
327+
assert lc.get_prompt(r"\d") == "(none)"
328+
assert lc.get_prompt(r"\f") == "(none)"
329+
330+
# 12. Windows path
331+
lc.connect("C:\\Users\\litecli\\litecli_test.db")
332+
assert lc.get_prompt(r"\d") == "C:\\Users\\litecli\\litecli_test.db"

0 commit comments

Comments
 (0)