replbase 0.0.17__tar.gz → 0.0.19__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.
- {replbase-0.0.17 → replbase-0.0.19}/PKG-INFO +1 -1
- {replbase-0.0.17 → replbase-0.0.19}/pyproject.toml +1 -1
- replbase-0.0.19/replbase/__init__.py +5 -0
- {replbase-0.0.17 → replbase-0.0.19}/replbase/repl_base.py +92 -26
- replbase-0.0.17/replbase/__init__.py +0 -5
- {replbase-0.0.17 → replbase-0.0.19}/LICENSE +0 -0
- {replbase-0.0.17 → replbase-0.0.19}/README.md +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "replbase"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.19"
|
|
8
8
|
description = "\"Combination of other REPL tools into a reusable class that generates a REPL\""
|
|
9
9
|
authors = [ "Joseph Bochinski <stirgejr@gmail.com>",]
|
|
10
10
|
license = "MIT"
|
|
@@ -23,11 +23,15 @@ from dataclasses import dataclass, field
|
|
|
23
23
|
from typing import Any, Callable, Literal
|
|
24
24
|
|
|
25
25
|
from prompt_toolkit import PromptSession
|
|
26
|
+
from prompt_toolkit.buffer import Buffer
|
|
27
|
+
from prompt_toolkit.document import Document
|
|
28
|
+
from prompt_toolkit.auto_suggest import AutoSuggest, Suggestion
|
|
26
29
|
from prompt_toolkit.history import FileHistory
|
|
27
30
|
from prompt_toolkit.completion import WordCompleter
|
|
28
31
|
from prompt_toolkit.styles import Style
|
|
29
32
|
from ptpython.repl import embed
|
|
30
33
|
from rich.console import Console
|
|
34
|
+
from rich.theme import Theme
|
|
31
35
|
|
|
32
36
|
# endregion Imports
|
|
33
37
|
|
|
@@ -45,6 +49,27 @@ def is_num_str(val: str) -> bool:
|
|
|
45
49
|
return bool(re.match(r"^-?\d+(\.\d+)?$", val))
|
|
46
50
|
|
|
47
51
|
|
|
52
|
+
@dataclass
|
|
53
|
+
class ReplTheme(Theme):
|
|
54
|
+
title: str = "bold cyan"
|
|
55
|
+
prompt: str = "bold green"
|
|
56
|
+
warn: str = "bold yellow"
|
|
57
|
+
error: str = "bold red"
|
|
58
|
+
cmd_name: str = "bold green"
|
|
59
|
+
cmd_desc: str = "cyan"
|
|
60
|
+
exit_kw: str = "bold green"
|
|
61
|
+
exit_str: str = "cyan"
|
|
62
|
+
greeting: str = "cyan"
|
|
63
|
+
addl_styles: dict = None
|
|
64
|
+
|
|
65
|
+
def __post_init__(self) -> None:
|
|
66
|
+
styles = dict(vars(self))
|
|
67
|
+
extras = styles.pop("addl_styles", {})
|
|
68
|
+
if extras:
|
|
69
|
+
styles.update(extras)
|
|
70
|
+
super().__init__(styles)
|
|
71
|
+
|
|
72
|
+
|
|
48
73
|
@dataclass
|
|
49
74
|
class ReplCommand:
|
|
50
75
|
"""Class definition for a provided CLI REPL command"""
|
|
@@ -65,7 +90,7 @@ class ReplBase:
|
|
|
65
90
|
title: str = None
|
|
66
91
|
"""Title of the CLI REPL Prompt"""
|
|
67
92
|
|
|
68
|
-
exit_keywords: list[str] = None
|
|
93
|
+
exit_keywords: list[str] = None
|
|
69
94
|
"""List of strings that cause the REPL to close, defaults to x, q,
|
|
70
95
|
exit, and quit"""
|
|
71
96
|
|
|
@@ -75,6 +100,8 @@ class ReplBase:
|
|
|
75
100
|
color_system: ColorSystem = None
|
|
76
101
|
"""Color syste for the rich console"""
|
|
77
102
|
|
|
103
|
+
theme: ReplTheme | dict = None
|
|
104
|
+
|
|
78
105
|
console: Console = None
|
|
79
106
|
""" Rich Console instance """
|
|
80
107
|
|
|
@@ -84,17 +111,19 @@ class ReplBase:
|
|
|
84
111
|
temp_file: str = None
|
|
85
112
|
"""Path to prompt temporary file"""
|
|
86
113
|
|
|
87
|
-
style: dict | Style = None
|
|
114
|
+
style: dict | Style = None
|
|
88
115
|
"""Style for the prompt"""
|
|
89
116
|
|
|
90
117
|
ignore_case: bool = None
|
|
91
118
|
"""Ignore case setting for the WordCompleter instance"""
|
|
92
119
|
|
|
93
|
-
commands: dict[str, ReplCommand] = None
|
|
120
|
+
commands: dict[str, ReplCommand] = None
|
|
94
121
|
"""Command dictionary for prompt_toolkit. Keys are command names,
|
|
95
122
|
values are the corresponding description/help text"""
|
|
96
123
|
|
|
97
|
-
parent: ReplBase = None
|
|
124
|
+
parent: ReplBase | dict = None
|
|
125
|
+
|
|
126
|
+
session: PromptSession = None
|
|
98
127
|
|
|
99
128
|
def __post_init__(self) -> None:
|
|
100
129
|
if isinstance(self.commands, dict):
|
|
@@ -110,20 +139,33 @@ class ReplBase:
|
|
|
110
139
|
self.exit_keywords = self.exit_keywords or ["x", "q", "exit", "quit"]
|
|
111
140
|
|
|
112
141
|
exit_kw_str = ", ".join(
|
|
113
|
-
f'[
|
|
142
|
+
f'[exit_kw]"{kw}"[/exit_kw]' for kw in self.exit_keywords
|
|
114
143
|
)
|
|
115
144
|
exit_kw_pref = "Type one of " if len(self.exit_keywords) > 1 else "Type "
|
|
116
|
-
exit_str = f"[
|
|
145
|
+
exit_str = f"[exit_str]{exit_kw_pref}{exit_kw_str} to exit[/exit_str]"
|
|
117
146
|
|
|
118
147
|
self.init_prompt = self.init_prompt or [
|
|
119
|
-
f"[
|
|
148
|
+
f"[title]<<| {self.title} |>>[/title]",
|
|
120
149
|
exit_str,
|
|
121
|
-
'[
|
|
150
|
+
'[greeting]Type [cmd_name]"help"[/cmd_name] to view available commands.[/greeting]',
|
|
122
151
|
]
|
|
123
152
|
|
|
124
153
|
self.color_system = self.color_system or "truecolor"
|
|
125
154
|
|
|
126
|
-
|
|
155
|
+
if isinstance(self.theme, dict):
|
|
156
|
+
props = list(dict(vars(ReplTheme())).keys())
|
|
157
|
+
init = {"addl_styles": {}}
|
|
158
|
+
for key, value in self.theme.items():
|
|
159
|
+
if key in props:
|
|
160
|
+
init[key] = value
|
|
161
|
+
else:
|
|
162
|
+
init["addl_styles"][key] = value
|
|
163
|
+
|
|
164
|
+
self.theme = ReplTheme(**init)
|
|
165
|
+
elif self.theme is None:
|
|
166
|
+
self.theme = ReplTheme()
|
|
167
|
+
|
|
168
|
+
self.console = Console(color_system=self.color_system, theme=self.theme)
|
|
127
169
|
|
|
128
170
|
self.history = self.history or os.path.expanduser(
|
|
129
171
|
"~/.config/.prompt_history"
|
|
@@ -187,8 +229,10 @@ class ReplBase:
|
|
|
187
229
|
"""Shortcut to console.print"""
|
|
188
230
|
self.console.print(*args)
|
|
189
231
|
|
|
190
|
-
def input(self, *args) -> str:
|
|
232
|
+
def input(self, *args, suggestions: AutoSuggest = None) -> str:
|
|
191
233
|
"""Shortcut to console.input"""
|
|
234
|
+
if self.session:
|
|
235
|
+
return self.session.prompt(*args, auto_suggest=suggestions)
|
|
192
236
|
return self.console.input(*args)
|
|
193
237
|
|
|
194
238
|
def input_int(self, *args) -> int:
|
|
@@ -254,6 +298,7 @@ class ReplBase:
|
|
|
254
298
|
help_txt: str = "",
|
|
255
299
|
use_parser: bool = False,
|
|
256
300
|
description: str = "",
|
|
301
|
+
# auto_suggest: AutoSuggest,
|
|
257
302
|
**def_kwargs,
|
|
258
303
|
) -> ReplCommand:
|
|
259
304
|
"""Add a command to the REPL
|
|
@@ -297,7 +342,7 @@ class ReplBase:
|
|
|
297
342
|
|
|
298
343
|
for cmd_name, cmd in self.commands.items():
|
|
299
344
|
self.print(
|
|
300
|
-
f"[
|
|
345
|
+
f"[cmd_name]{cmd_name}:[/cmd_name] [cmd_desc]{cmd.help_txt}[/cmd_desc]"
|
|
301
346
|
)
|
|
302
347
|
|
|
303
348
|
def print_prompt(self) -> None:
|
|
@@ -316,40 +361,35 @@ class ReplBase:
|
|
|
316
361
|
self.get_cmd_names(), ignore_case=self.ignore_case
|
|
317
362
|
)
|
|
318
363
|
|
|
319
|
-
session = PromptSession(
|
|
364
|
+
self.session = PromptSession(
|
|
320
365
|
completer=completer,
|
|
321
366
|
style=self.style,
|
|
322
367
|
history=FileHistory(self.history),
|
|
323
368
|
tempfile=self.temp_file,
|
|
369
|
+
auto_suggest=SuggestFromLs,
|
|
324
370
|
)
|
|
325
371
|
|
|
326
372
|
self.print_prompt()
|
|
327
373
|
|
|
328
374
|
while True:
|
|
329
375
|
try:
|
|
330
|
-
user_input = session.prompt("> ", complete_while_typing=True)
|
|
376
|
+
user_input = self.session.prompt("> ", complete_while_typing=True)
|
|
331
377
|
|
|
332
378
|
if user_input.lower() in ["help", "h"]:
|
|
333
379
|
self.show_help()
|
|
334
380
|
elif user_input.lower() in self.exit_keywords:
|
|
335
|
-
self.print(
|
|
336
|
-
f"[bold yellow]Exiting REPL ({self.title})...[/bold yellow]"
|
|
337
|
-
)
|
|
381
|
+
self.print(f"[warn]Exiting REPL ({self.title})...[/warn]")
|
|
338
382
|
break
|
|
339
383
|
else:
|
|
340
384
|
args = shlex.split(user_input)
|
|
341
385
|
if not args:
|
|
342
|
-
self.print(
|
|
343
|
-
"[bold yellow][WARNING]: No command provided[/bold yellow]"
|
|
344
|
-
)
|
|
386
|
+
self.print("[warn][WARNING]: No command provided[/warn]")
|
|
345
387
|
continue
|
|
346
388
|
|
|
347
389
|
cmd = self.commands.get(args[0])
|
|
348
390
|
|
|
349
391
|
if not cmd:
|
|
350
|
-
self.print(
|
|
351
|
-
"[bold yellow][WARNING]: Invalid command[/bold yellow]"
|
|
352
|
-
)
|
|
392
|
+
self.print("[warn][WARNING]: Invalid command[/warn]")
|
|
353
393
|
continue
|
|
354
394
|
|
|
355
395
|
cmd_args = args[1:]
|
|
@@ -372,18 +412,44 @@ class ReplBase:
|
|
|
372
412
|
cmd.command(*cmd_args)
|
|
373
413
|
else:
|
|
374
414
|
self.print(
|
|
375
|
-
"[
|
|
415
|
+
"[warn][WARNING]: No function provided for command[/warn]"
|
|
376
416
|
)
|
|
377
417
|
except (EOFError, KeyboardInterrupt):
|
|
378
|
-
self.print(
|
|
379
|
-
f"[bold yellow]Exiting REPL ({self.title})...[/bold yellow]"
|
|
380
|
-
)
|
|
418
|
+
self.print(f"[warn]Exiting REPL ({self.title})...[/warn]")
|
|
381
419
|
break
|
|
382
420
|
|
|
383
421
|
if self.parent:
|
|
384
422
|
self.parent.print_prompt()
|
|
385
423
|
|
|
386
424
|
|
|
425
|
+
class SuggestFromLs(AutoSuggest):
|
|
426
|
+
|
|
427
|
+
def get_suggestion(
|
|
428
|
+
self, buffer: Buffer, document: Document
|
|
429
|
+
) -> Suggestion | None:
|
|
430
|
+
files = os.listdir()
|
|
431
|
+
|
|
432
|
+
# Consider only the last line for the suggestion.
|
|
433
|
+
text = document.text.rsplit("\n", 1)[-1]
|
|
434
|
+
|
|
435
|
+
if text.strip():
|
|
436
|
+
for item in files:
|
|
437
|
+
if item.lower().startswith(text.lower()):
|
|
438
|
+
return Suggestion(item[len(text) :])
|
|
439
|
+
return None
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
@dataclass
|
|
443
|
+
class TestRepl(ReplBase):
|
|
444
|
+
def __post_init__(self) -> None:
|
|
445
|
+
super().__post_init__()
|
|
446
|
+
self.add_command("test_cmd", self.test_cmd, help_txt="Test command")
|
|
447
|
+
|
|
448
|
+
def test_cmd(self) -> None:
|
|
449
|
+
user_input = self.input("Testing prompt: ", suggestions=SuggestFromLs())
|
|
450
|
+
self.print(user_input)
|
|
451
|
+
|
|
452
|
+
|
|
387
453
|
# endregion Classes
|
|
388
454
|
|
|
389
455
|
|
|
File without changes
|
|
File without changes
|