replbase 0.0.16__tar.gz → 0.0.18__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.16 → replbase-0.0.18}/PKG-INFO +1 -1
- {replbase-0.0.16 → replbase-0.0.18}/pyproject.toml +1 -1
- replbase-0.0.18/replbase/__init__.py +5 -0
- {replbase-0.0.16 → replbase-0.0.18}/replbase/repl_base.py +90 -26
- replbase-0.0.16/replbase/__init__.py +0 -5
- {replbase-0.0.16 → replbase-0.0.18}/LICENSE +0 -0
- {replbase-0.0.16 → replbase-0.0.18}/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.18"
|
|
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:
|
|
@@ -297,7 +341,7 @@ class ReplBase:
|
|
|
297
341
|
|
|
298
342
|
for cmd_name, cmd in self.commands.items():
|
|
299
343
|
self.print(
|
|
300
|
-
f"[
|
|
344
|
+
f"[cmd_name]{cmd_name}:[/cmd_name] [cmd_desc]{cmd.help_txt}[/cmd_desc]"
|
|
301
345
|
)
|
|
302
346
|
|
|
303
347
|
def print_prompt(self) -> None:
|
|
@@ -316,7 +360,7 @@ class ReplBase:
|
|
|
316
360
|
self.get_cmd_names(), ignore_case=self.ignore_case
|
|
317
361
|
)
|
|
318
362
|
|
|
319
|
-
session = PromptSession(
|
|
363
|
+
self.session = PromptSession(
|
|
320
364
|
completer=completer,
|
|
321
365
|
style=self.style,
|
|
322
366
|
history=FileHistory(self.history),
|
|
@@ -327,29 +371,23 @@ class ReplBase:
|
|
|
327
371
|
|
|
328
372
|
while True:
|
|
329
373
|
try:
|
|
330
|
-
user_input = session.prompt("> ", complete_while_typing=True)
|
|
374
|
+
user_input = self.session.prompt("> ", complete_while_typing=True)
|
|
331
375
|
|
|
332
376
|
if user_input.lower() in ["help", "h"]:
|
|
333
377
|
self.show_help()
|
|
334
378
|
elif user_input.lower() in self.exit_keywords:
|
|
335
|
-
self.print(
|
|
336
|
-
f"[bold yellow]Exiting REPL ({self.title})...[/bold yellow]"
|
|
337
|
-
)
|
|
379
|
+
self.print(f"[warn]Exiting REPL ({self.title})...[/warn]")
|
|
338
380
|
break
|
|
339
381
|
else:
|
|
340
382
|
args = shlex.split(user_input)
|
|
341
383
|
if not args:
|
|
342
|
-
self.print(
|
|
343
|
-
"[bold yellow][WARNING]: No command provided[/bold yellow]"
|
|
344
|
-
)
|
|
384
|
+
self.print("[warn][WARNING]: No command provided[/warn]")
|
|
345
385
|
continue
|
|
346
386
|
|
|
347
387
|
cmd = self.commands.get(args[0])
|
|
348
388
|
|
|
349
389
|
if not cmd:
|
|
350
|
-
self.print(
|
|
351
|
-
"[bold yellow][WARNING]: Invalid command[/bold yellow]"
|
|
352
|
-
)
|
|
390
|
+
self.print("[warn][WARNING]: Invalid command[/warn]")
|
|
353
391
|
continue
|
|
354
392
|
|
|
355
393
|
cmd_args = args[1:]
|
|
@@ -372,18 +410,44 @@ class ReplBase:
|
|
|
372
410
|
cmd.command(*cmd_args)
|
|
373
411
|
else:
|
|
374
412
|
self.print(
|
|
375
|
-
"[
|
|
413
|
+
"[warn][WARNING]: No function provided for command[/warn]"
|
|
376
414
|
)
|
|
377
415
|
except (EOFError, KeyboardInterrupt):
|
|
378
|
-
self.print(
|
|
379
|
-
f"[bold yellow]Exiting REPL ({self.title})...[/bold yellow]"
|
|
380
|
-
)
|
|
416
|
+
self.print(f"[warn]Exiting REPL ({self.title})...[/warn]")
|
|
381
417
|
break
|
|
382
418
|
|
|
383
419
|
if self.parent:
|
|
384
420
|
self.parent.print_prompt()
|
|
385
421
|
|
|
386
422
|
|
|
423
|
+
class SuggestFromLs(AutoSuggest):
|
|
424
|
+
|
|
425
|
+
def get_suggestion(
|
|
426
|
+
self, buffer: Buffer, document: Document
|
|
427
|
+
) -> Suggestion | None:
|
|
428
|
+
files = os.listdir()
|
|
429
|
+
|
|
430
|
+
# Consider only the last line for the suggestion.
|
|
431
|
+
text = document.text.rsplit("\n", 1)[-1]
|
|
432
|
+
|
|
433
|
+
if text.strip():
|
|
434
|
+
for item in files:
|
|
435
|
+
if item.lower().startswith(text.lower()):
|
|
436
|
+
return Suggestion(item[len(text) :])
|
|
437
|
+
return None
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
@dataclass
|
|
441
|
+
class TestRepl(ReplBase):
|
|
442
|
+
def __post_init__(self) -> None:
|
|
443
|
+
super().__post_init__()
|
|
444
|
+
self.add_command("test_cmd", self.test_cmd, help_txt="Test command")
|
|
445
|
+
|
|
446
|
+
def test_cmd(self) -> None:
|
|
447
|
+
user_input = self.input("Testing prompt: ", suggestions=SuggestFromLs())
|
|
448
|
+
self.print(user_input)
|
|
449
|
+
|
|
450
|
+
|
|
387
451
|
# endregion Classes
|
|
388
452
|
|
|
389
453
|
|
|
File without changes
|
|
File without changes
|