webscout 7.8__py3-none-any.whl → 8.0__py3-none-any.whl
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.
Potentially problematic release.
This version of webscout might be problematic. Click here for more details.
- webscout/Bard.py +5 -25
- webscout/DWEBS.py +476 -476
- webscout/Extra/GitToolkit/__init__.py +10 -0
- webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
- webscout/Extra/GitToolkit/gitapi/user.py +96 -0
- webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
- webscout/Extra/YTToolkit/ytapi/video.py +232 -103
- webscout/Extra/__init__.py +2 -0
- webscout/Extra/autocoder/__init__.py +1 -1
- webscout/Extra/autocoder/{rawdog.py → autocoder.py} +849 -849
- webscout/Extra/tempmail/__init__.py +26 -0
- webscout/Extra/tempmail/async_utils.py +141 -0
- webscout/Extra/tempmail/base.py +156 -0
- webscout/Extra/tempmail/cli.py +187 -0
- webscout/Extra/tempmail/mail_tm.py +361 -0
- webscout/Extra/tempmail/temp_mail_io.py +292 -0
- webscout/Provider/AISEARCH/__init__.py +5 -1
- webscout/Provider/AISEARCH/hika_search.py +194 -0
- webscout/Provider/AISEARCH/monica_search.py +246 -0
- webscout/Provider/AISEARCH/scira_search.py +320 -0
- webscout/Provider/AISEARCH/webpilotai_search.py +281 -0
- webscout/Provider/AllenAI.py +255 -122
- webscout/Provider/DeepSeek.py +1 -2
- webscout/Provider/Deepinfra.py +296 -286
- webscout/Provider/ElectronHub.py +709 -716
- webscout/Provider/ExaAI.py +261 -0
- webscout/Provider/ExaChat.py +28 -6
- webscout/Provider/Gemini.py +167 -165
- webscout/Provider/GithubChat.py +2 -1
- webscout/Provider/Groq.py +38 -24
- webscout/Provider/LambdaChat.py +2 -1
- webscout/Provider/Netwrck.py +3 -2
- webscout/Provider/OpenGPT.py +199 -0
- webscout/Provider/PI.py +39 -24
- webscout/Provider/TextPollinationsAI.py +232 -230
- webscout/Provider/Youchat.py +326 -296
- webscout/Provider/__init__.py +10 -4
- webscout/Provider/ai4chat.py +58 -56
- webscout/Provider/akashgpt.py +34 -22
- webscout/Provider/copilot.py +427 -427
- webscout/Provider/freeaichat.py +9 -2
- webscout/Provider/labyrinth.py +121 -20
- webscout/Provider/llmchatco.py +306 -0
- webscout/Provider/scira_chat.py +271 -0
- webscout/Provider/typefully.py +280 -0
- webscout/Provider/uncovr.py +312 -299
- webscout/Provider/yep.py +64 -12
- webscout/__init__.py +38 -36
- webscout/cli.py +293 -293
- webscout/conversation.py +350 -17
- webscout/litprinter/__init__.py +59 -667
- webscout/optimizers.py +419 -419
- webscout/update_checker.py +14 -12
- webscout/version.py +1 -1
- webscout/webscout_search.py +1346 -1282
- webscout/webscout_search_async.py +877 -813
- {webscout-7.8.dist-info → webscout-8.0.dist-info}/METADATA +44 -39
- {webscout-7.8.dist-info → webscout-8.0.dist-info}/RECORD +63 -46
- webscout/Provider/DARKAI.py +0 -225
- webscout/Provider/EDITEE.py +0 -192
- webscout/litprinter/colors.py +0 -54
- {webscout-7.8.dist-info → webscout-8.0.dist-info}/LICENSE.md +0 -0
- {webscout-7.8.dist-info → webscout-8.0.dist-info}/WHEEL +0 -0
- {webscout-7.8.dist-info → webscout-8.0.dist-info}/entry_points.txt +0 -0
- {webscout-7.8.dist-info → webscout-8.0.dist-info}/top_level.txt +0 -0
webscout/litprinter/__init__.py
CHANGED
|
@@ -1,667 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
H1 = Colors.BOLD + Colors.BLUE
|
|
61
|
-
H2 = Colors.BOLD + Colors.CYAN
|
|
62
|
-
H3 = Colors.BOLD + Colors.GREEN
|
|
63
|
-
BOLD = Colors.BOLD
|
|
64
|
-
ITALIC = Colors.ITALIC
|
|
65
|
-
CODE = Colors.YELLOW
|
|
66
|
-
LINK = Colors.BLUE + Colors.UNDERLINE
|
|
67
|
-
LIST_BULLET = Colors.CYAN + "•" + Colors.RESET
|
|
68
|
-
QUOTE = Colors.GRAY
|
|
69
|
-
STRIKE = Colors.STRIKE
|
|
70
|
-
TABLE = Colors.GREEN
|
|
71
|
-
TASK = Colors.YELLOW
|
|
72
|
-
DETAILS = Colors.MAGENTA
|
|
73
|
-
|
|
74
|
-
class ThemeStyles:
|
|
75
|
-
SUCCESS = f"{Colors.GREEN}{Colors.BOLD}"
|
|
76
|
-
ERROR = f"{Colors.RED}{Colors.BOLD}"
|
|
77
|
-
WARNING = f"{Colors.YELLOW}{Colors.BOLD}"
|
|
78
|
-
INFO = f"{Colors.BLUE}{Colors.BOLD}"
|
|
79
|
-
DEBUG = f"{Colors.MAGENTA}"
|
|
80
|
-
CODE = f"{Colors.CYAN}"
|
|
81
|
-
|
|
82
|
-
class HoverInfo:
|
|
83
|
-
"""Hover information for different elements."""
|
|
84
|
-
BANNER = "A fancy banner to make your output pop!"
|
|
85
|
-
SUCCESS = "Something went right! "
|
|
86
|
-
ERROR = "Oops! Something went wrong "
|
|
87
|
-
WARNING = "Heads up! Something needs attention "
|
|
88
|
-
INFO = "Just some helpful info "
|
|
89
|
-
TABLE = "Data organized in rows and columns "
|
|
90
|
-
TREE = "Hierarchical data visualization "
|
|
91
|
-
JSON = "Pretty-printed JSON data "
|
|
92
|
-
CODE = "Syntax-highlighted code block "
|
|
93
|
-
|
|
94
|
-
class ProgressBar:
|
|
95
|
-
def __init__(self, total: int, width: int = 40, prefix: str = '', suffix: str = ''):
|
|
96
|
-
self.total = total
|
|
97
|
-
self.width = width
|
|
98
|
-
self.prefix = prefix
|
|
99
|
-
self.suffix = suffix
|
|
100
|
-
self.current = 0
|
|
101
|
-
|
|
102
|
-
def update(self, current: int):
|
|
103
|
-
self.current = current
|
|
104
|
-
filled = int(self.width * current / self.total)
|
|
105
|
-
bar = f"{Colors.GREEN}{'█' * filled}{Colors.RESET}{'░' * (self.width - filled)}"
|
|
106
|
-
percent = f"{Colors.CYAN}{int(100 * current / self.total)}%{Colors.RESET}"
|
|
107
|
-
print(f'\r{self.prefix} |{bar}| {percent} {self.suffix}', end='', flush=True)
|
|
108
|
-
if current >= self.total:
|
|
109
|
-
print()
|
|
110
|
-
|
|
111
|
-
class Spinner:
|
|
112
|
-
def __init__(self, message: str = ''):
|
|
113
|
-
self.message = message
|
|
114
|
-
self.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
115
|
-
self.running = False
|
|
116
|
-
self.thread = None
|
|
117
|
-
|
|
118
|
-
def spin(self):
|
|
119
|
-
while self.running:
|
|
120
|
-
for frame in self.frames:
|
|
121
|
-
if not self.running:
|
|
122
|
-
break
|
|
123
|
-
print(f'\r{Colors.CYAN}{frame}{Colors.RESET} {self.message}', end='', flush=True)
|
|
124
|
-
time.sleep(0.1)
|
|
125
|
-
|
|
126
|
-
def __enter__(self):
|
|
127
|
-
self.start()
|
|
128
|
-
return self
|
|
129
|
-
|
|
130
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
131
|
-
self.stop()
|
|
132
|
-
|
|
133
|
-
def start(self):
|
|
134
|
-
self.running = True
|
|
135
|
-
self.thread = threading.Thread(target=self.spin)
|
|
136
|
-
self.thread.start()
|
|
137
|
-
|
|
138
|
-
def stop(self):
|
|
139
|
-
self.running = False
|
|
140
|
-
if self.thread:
|
|
141
|
-
self.thread.join()
|
|
142
|
-
print('\r' + ' ' * (len(self.message) + 2), end='', flush=True)
|
|
143
|
-
print('\r', end='')
|
|
144
|
-
|
|
145
|
-
class LitPrinter:
|
|
146
|
-
def __init__(self,
|
|
147
|
-
file: TextIO = sys.stdout,
|
|
148
|
-
theme: Optional[dict] = None,
|
|
149
|
-
indent_size: int = 4,
|
|
150
|
-
buffer_size: int = 1024,
|
|
151
|
-
syntax_theme: Optional[Dict[str, str]] = None,
|
|
152
|
-
markdown_theme: Optional[Dict[str, str]] = None):
|
|
153
|
-
self.file = file
|
|
154
|
-
self.indent_size = indent_size
|
|
155
|
-
self.buffer_size = buffer_size
|
|
156
|
-
self._terminal_width = shutil.get_terminal_size().columns
|
|
157
|
-
self._last_line = ""
|
|
158
|
-
self._spinner_active = False
|
|
159
|
-
self._progress_active = False
|
|
160
|
-
|
|
161
|
-
# Default theme
|
|
162
|
-
self.theme = theme or {
|
|
163
|
-
"str": Colors.WHITE,
|
|
164
|
-
"int": Colors.CYAN,
|
|
165
|
-
"float": Colors.CYAN,
|
|
166
|
-
"bool": Colors.YELLOW,
|
|
167
|
-
"list": Colors.MAGENTA,
|
|
168
|
-
"dict": Colors.BLUE,
|
|
169
|
-
"none": Colors.RED,
|
|
170
|
-
"timestamp": Colors.GREEN,
|
|
171
|
-
"key": Colors.YELLOW,
|
|
172
|
-
"bracket": Colors.BLUE,
|
|
173
|
-
"comma": Colors.WHITE,
|
|
174
|
-
"colon": Colors.WHITE,
|
|
175
|
-
"url": f"{Colors.BLUE}{Colors.UNDERLINE}",
|
|
176
|
-
"number": Colors.CYAN,
|
|
177
|
-
"special": Colors.MAGENTA + Colors.BOLD,
|
|
178
|
-
"error": Colors.RED + Colors.BOLD,
|
|
179
|
-
"warning": Colors.YELLOW + Colors.BOLD,
|
|
180
|
-
"success": Colors.GREEN + Colors.BOLD,
|
|
181
|
-
"info": Colors.BLUE + Colors.BOLD,
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
self.syntax_theme = syntax_theme or vars(SyntaxTheme)
|
|
185
|
-
self.markdown_theme = markdown_theme or vars(MarkdownTheme)
|
|
186
|
-
|
|
187
|
-
def _get_terminal_width(self) -> int:
|
|
188
|
-
"""Get the terminal width or default to 80."""
|
|
189
|
-
try:
|
|
190
|
-
width = shutil.get_terminal_size().columns
|
|
191
|
-
return width if width > 0 else 80
|
|
192
|
-
except:
|
|
193
|
-
return 80
|
|
194
|
-
|
|
195
|
-
def _format_dict(self, d: dict, indent_level: int = 0) -> str:
|
|
196
|
-
"""Format dictionary with proper indentation and colors."""
|
|
197
|
-
if not d:
|
|
198
|
-
return f"{self.theme['bracket']}{{}}{Colors.RESET}"
|
|
199
|
-
|
|
200
|
-
indent = " " * (self.indent_size * indent_level)
|
|
201
|
-
next_indent = " " * (self.indent_size * (indent_level + 1))
|
|
202
|
-
|
|
203
|
-
lines = [f"{self.theme['bracket']}{{{Colors.RESET}"]
|
|
204
|
-
|
|
205
|
-
for i, (key, value) in enumerate(d.items()):
|
|
206
|
-
# Format key with quotes if it's a string
|
|
207
|
-
if isinstance(key, str):
|
|
208
|
-
formatted_key = f"{self.theme['key']}'{key}'{Colors.RESET}"
|
|
209
|
-
else:
|
|
210
|
-
formatted_key = f"{self.theme['key']}{key}{Colors.RESET}"
|
|
211
|
-
|
|
212
|
-
# Format value based on type
|
|
213
|
-
if isinstance(value, dict):
|
|
214
|
-
formatted_value = self._format_dict(value, indent_level + 1)
|
|
215
|
-
elif isinstance(value, str):
|
|
216
|
-
# Special handling for URLs
|
|
217
|
-
if any(url_prefix in value.lower() for url_prefix in ['http://', 'https://', 'www.']):
|
|
218
|
-
formatted_value = f"{self.theme['url']}{value}{Colors.RESET}"
|
|
219
|
-
else:
|
|
220
|
-
# Word wrap long strings
|
|
221
|
-
if len(value) > 80:
|
|
222
|
-
wrapped = textwrap.fill(value, width=80, subsequent_indent=next_indent + " ")
|
|
223
|
-
formatted_value = f"{self.theme['str']}'{wrapped}'{Colors.RESET}"
|
|
224
|
-
else:
|
|
225
|
-
formatted_value = f"{self.theme['str']}'{value}'{Colors.RESET}"
|
|
226
|
-
elif isinstance(value, (int, float)):
|
|
227
|
-
formatted_value = f"{self.theme['number']}{value}{Colors.RESET}"
|
|
228
|
-
elif isinstance(value, bool):
|
|
229
|
-
formatted_value = f"{self.theme['bool']}{value}{Colors.RESET}"
|
|
230
|
-
elif value is None:
|
|
231
|
-
formatted_value = f"{self.theme['none']}None{Colors.RESET}"
|
|
232
|
-
else:
|
|
233
|
-
formatted_value = self._format_value(value)
|
|
234
|
-
|
|
235
|
-
# Add comma if not last item
|
|
236
|
-
comma = f"{self.theme['comma']},{Colors.RESET}" if i < len(d) - 1 else ""
|
|
237
|
-
|
|
238
|
-
lines.append(f"{next_indent}{formatted_key}{self.theme['colon']}: {Colors.RESET}{formatted_value}{comma}")
|
|
239
|
-
|
|
240
|
-
lines.append(f"{indent}{self.theme['bracket']}}}{Colors.RESET}")
|
|
241
|
-
return '\n'.join(lines)
|
|
242
|
-
|
|
243
|
-
def _format_sequence(self, seq: Sequence, indent_level: int = 0) -> str:
|
|
244
|
-
"""Format sequences (lists, tuples, sets) with proper indentation."""
|
|
245
|
-
if not seq:
|
|
246
|
-
return f"{self.theme['bracket']}[]{Colors.RESET}"
|
|
247
|
-
|
|
248
|
-
indent = " " * (self.indent_size * indent_level)
|
|
249
|
-
next_indent = " " * (self.indent_size * (indent_level + 1))
|
|
250
|
-
|
|
251
|
-
lines = [f"{self.theme['bracket']}[{Colors.RESET}"]
|
|
252
|
-
|
|
253
|
-
for i, item in enumerate(seq):
|
|
254
|
-
formatted_item = self._format_value(item, indent_level + 1)
|
|
255
|
-
comma = f"{self.theme['comma']},{Colors.RESET}" if i < len(seq) - 1 else ""
|
|
256
|
-
lines.append(f"{next_indent}{formatted_item}{comma}")
|
|
257
|
-
|
|
258
|
-
lines.append(f"{indent}{self.theme['bracket']}]{Colors.RESET}")
|
|
259
|
-
return '\n'.join(lines)
|
|
260
|
-
|
|
261
|
-
def _format_value(self, value: Any, indent_level: int = 0) -> str:
|
|
262
|
-
"""Enhanced format for any value with proper indentation and styling."""
|
|
263
|
-
if value is None:
|
|
264
|
-
return f"{self.theme['none']}None{Colors.RESET}"
|
|
265
|
-
|
|
266
|
-
if isinstance(value, dict):
|
|
267
|
-
return self._format_dict(value, indent_level)
|
|
268
|
-
|
|
269
|
-
if isinstance(value, (list, tuple, set)):
|
|
270
|
-
return self._format_sequence(value, indent_level)
|
|
271
|
-
|
|
272
|
-
if isinstance(value, str):
|
|
273
|
-
return str(value)
|
|
274
|
-
|
|
275
|
-
if isinstance(value, bool):
|
|
276
|
-
return f"{self.theme['bool']}{str(value)}{Colors.RESET}"
|
|
277
|
-
|
|
278
|
-
if isinstance(value, (int, float)):
|
|
279
|
-
return f"{self.theme['number']}{str(value)}{Colors.RESET}"
|
|
280
|
-
|
|
281
|
-
if hasattr(value, '__dict__'):
|
|
282
|
-
return self._format_dict(value.__dict__, indent_level)
|
|
283
|
-
|
|
284
|
-
return str(value)
|
|
285
|
-
|
|
286
|
-
def _highlight_code(self, code: str, language: str = "python") -> str:
|
|
287
|
-
"""Print code with that extra drip """
|
|
288
|
-
if PYGMENTS_AVAILABLE:
|
|
289
|
-
try:
|
|
290
|
-
lexer = get_lexer_by_name(language)
|
|
291
|
-
formatter = Terminal256Formatter(style='monokai')
|
|
292
|
-
return highlight(code, lexer, formatter)
|
|
293
|
-
except:
|
|
294
|
-
pass
|
|
295
|
-
|
|
296
|
-
if language != 'python':
|
|
297
|
-
return code
|
|
298
|
-
|
|
299
|
-
lines = []
|
|
300
|
-
for line in code.split('\n'):
|
|
301
|
-
if '#' in line:
|
|
302
|
-
code_part, comment_part = line.split('#', 1)
|
|
303
|
-
line = code_part + f"{self.syntax_theme['COMMENT']}#{comment_part}{Colors.RESET}"
|
|
304
|
-
|
|
305
|
-
line = re.sub(r'(".*?"|\'.*?\')',
|
|
306
|
-
f"{self.syntax_theme['STRING']}\\1{Colors.RESET}", line)
|
|
307
|
-
|
|
308
|
-
line = re.sub(r'\b(\d+)\b',
|
|
309
|
-
f"{self.syntax_theme['NUMBER']}\\1{Colors.RESET}", line)
|
|
310
|
-
|
|
311
|
-
keywords = ['def', 'class', 'if', 'else', 'elif', 'for', 'while', 'try',
|
|
312
|
-
'except', 'finally', 'with', 'as', 'import', 'from', 'return']
|
|
313
|
-
|
|
314
|
-
for keyword in keywords:
|
|
315
|
-
line = re.sub(f'\\b{keyword}\\b',
|
|
316
|
-
f"{self.syntax_theme['KEYWORD']}{keyword}{Colors.RESET}", line)
|
|
317
|
-
|
|
318
|
-
lines.append(line)
|
|
319
|
-
|
|
320
|
-
return '\n'.join(lines)
|
|
321
|
-
|
|
322
|
-
def _format_markdown_stream(self, text: str) -> str:
|
|
323
|
-
"""Enhanced markdown formatting for streaming mode."""
|
|
324
|
-
# Headers with emoji flair
|
|
325
|
-
text = re.sub(r'^# (.+)$', f"{self.markdown_theme['H1']}🔥 \\1{Colors.RESET}", text, flags=re.M)
|
|
326
|
-
text = re.sub(r'^## (.+)$', f"{self.markdown_theme['H2']}✨ \\1{Colors.RESET}", text, flags=re.M)
|
|
327
|
-
text = re.sub(r'^### (.+)$', f"{self.markdown_theme['H3']}💫 \\1{Colors.RESET}", text, flags=re.M)
|
|
328
|
-
|
|
329
|
-
# Bold, italic, and combined with multiple styles
|
|
330
|
-
text = re.sub(r'\*\*\*(.+?)\*\*\*', f"{Colors.BOLD}{Colors.ITALIC}\\1{Colors.RESET}", text)
|
|
331
|
-
text = re.sub(r'\*\*(.+?)\*\*', f"{Colors.BOLD}\\1{Colors.RESET}", text)
|
|
332
|
-
text = re.sub(r'\*(.+?)\*', f"{Colors.ITALIC}\\1{Colors.RESET}", text)
|
|
333
|
-
text = re.sub(r'__(.+?)__', f"{Colors.BOLD}\\1{Colors.RESET}", text)
|
|
334
|
-
text = re.sub(r'_(.+?)_', f"{Colors.ITALIC}\\1{Colors.RESET}", text)
|
|
335
|
-
|
|
336
|
-
# Code blocks and inline code
|
|
337
|
-
text = re.sub(r'```(\w+)?\n(.*?)\n```', lambda m: self._highlight_code(m.group(2), m.group(1) or 'text'), text, flags=re.S)
|
|
338
|
-
text = re.sub(r'`(.+?)`', f"{Colors.CYAN}\\1{Colors.RESET}", text)
|
|
339
|
-
|
|
340
|
-
# Lists with proper indentation and bullets
|
|
341
|
-
lines = text.split('\n')
|
|
342
|
-
formatted_lines = []
|
|
343
|
-
for i, line in enumerate(lines):
|
|
344
|
-
# Match different bullet point styles
|
|
345
|
-
bullet_match = re.match(r'^(\s*)([-•*]|\d+\.) (.+)$', line)
|
|
346
|
-
if bullet_match:
|
|
347
|
-
indent, bullet, content = bullet_match.groups()
|
|
348
|
-
indent_level = len(indent) // 2
|
|
349
|
-
|
|
350
|
-
# Choose bullet style based on nesting level
|
|
351
|
-
if indent_level == 0:
|
|
352
|
-
bullet_style = "•"
|
|
353
|
-
elif indent_level == 1:
|
|
354
|
-
bullet_style = "◦"
|
|
355
|
-
else:
|
|
356
|
-
bullet_style = "▪"
|
|
357
|
-
|
|
358
|
-
# Format the line with proper indentation and bullet
|
|
359
|
-
formatted_line = f"{' ' * (indent_level * 2)}{Colors.CYAN}{bullet_style}{Colors.RESET} {content}"
|
|
360
|
-
formatted_lines.append(formatted_line)
|
|
361
|
-
else:
|
|
362
|
-
formatted_lines.append(line)
|
|
363
|
-
text = '\n'.join(formatted_lines)
|
|
364
|
-
|
|
365
|
-
# Links with underline
|
|
366
|
-
text = re.sub(r'\[(.+?)\]\((.+?)\)', f"{Colors.BLUE}{Colors.UNDERLINE}\\1{Colors.RESET}", text)
|
|
367
|
-
|
|
368
|
-
# Blockquotes with style
|
|
369
|
-
text = re.sub(r'^> (.+)$', f"{self.markdown_theme['QUOTE']}│ \\1{Colors.RESET}", text, flags=re.M)
|
|
370
|
-
|
|
371
|
-
# Strikethrough
|
|
372
|
-
text = re.sub(r'~~(.+?)~~', f"{Colors.STRIKE}\\1{Colors.RESET}", text)
|
|
373
|
-
|
|
374
|
-
# Task lists with fancy checkboxes
|
|
375
|
-
text = re.sub(r'- \[ \] (.+)$', f"{self.markdown_theme['TASK']}☐ \\1{Colors.RESET}", text, flags=re.M)
|
|
376
|
-
text = re.sub(r'- \[x\] (.+)$', f"{self.markdown_theme['TASK']}☑ \\1{Colors.RESET}", text, flags=re.M)
|
|
377
|
-
|
|
378
|
-
# Tables with borders
|
|
379
|
-
table_pattern = r'\|(.+?)\|[\r\n]+\|[-:| ]+\|[\r\n]+((?:\|.+?\|[\r\n]+)+)'
|
|
380
|
-
text = re.sub(table_pattern, self._format_table_markdown, text, flags=re.M)
|
|
381
|
-
|
|
382
|
-
return text
|
|
383
|
-
|
|
384
|
-
def _format_table_markdown(self, match) -> str:
|
|
385
|
-
"""Format markdown tables with style."""
|
|
386
|
-
header = [cell.strip() for cell in match.group(1).split('|') if cell.strip()]
|
|
387
|
-
rows = []
|
|
388
|
-
for row in match.group(2).strip().split('\n'):
|
|
389
|
-
cells = [cell.strip() for cell in row.split('|')[1:-1]]
|
|
390
|
-
if cells:
|
|
391
|
-
rows.append(cells)
|
|
392
|
-
|
|
393
|
-
# Get column widths
|
|
394
|
-
widths = [max(len(str(row[i])) for row in [header] + rows) for i in range(len(header))]
|
|
395
|
-
|
|
396
|
-
# Build table
|
|
397
|
-
result = []
|
|
398
|
-
# Header
|
|
399
|
-
result.append('┌' + '┬'.join('─' * (w + 2) for w in widths) + '┐')
|
|
400
|
-
result.append('│ ' + ' │ '.join(f"{h:<{w}}" for h, w in zip(header, widths)) + ' │')
|
|
401
|
-
result.append('├' + '┼'.join('─' * (w + 2) for w in widths) + '┤')
|
|
402
|
-
# Rows
|
|
403
|
-
for row in rows:
|
|
404
|
-
result.append('│ ' + ' │ '.join(f"{str(c):<{w}}" for c, w in zip(row, widths)) + ' │')
|
|
405
|
-
result.append('└' + '┴'.join('─' * (w + 2) for w in widths) + '┘')
|
|
406
|
-
|
|
407
|
-
return '\n'.join(result)
|
|
408
|
-
|
|
409
|
-
def print(self, *args,
|
|
410
|
-
# Builtin print compatibility
|
|
411
|
-
sep: str = " ",
|
|
412
|
-
end: str = "\n",
|
|
413
|
-
file: Optional[TextIO] = None,
|
|
414
|
-
flush: bool = True,
|
|
415
|
-
|
|
416
|
-
# Styling options
|
|
417
|
-
style: Optional[str] = None,
|
|
418
|
-
color: Optional[str] = None,
|
|
419
|
-
bg_color: Optional[str] = None,
|
|
420
|
-
bold: bool = False,
|
|
421
|
-
italic: bool = False,
|
|
422
|
-
underline: bool = False,
|
|
423
|
-
blink: bool = False,
|
|
424
|
-
strike: bool = False,
|
|
425
|
-
dim: bool = False,
|
|
426
|
-
reverse: bool = False,
|
|
427
|
-
|
|
428
|
-
# Layout options
|
|
429
|
-
markdown: Optional[bool] = None,
|
|
430
|
-
highlight: bool = False,
|
|
431
|
-
center: bool = False,
|
|
432
|
-
indent: int = 0,
|
|
433
|
-
prefix: Optional[str] = None,
|
|
434
|
-
suffix: Optional[str] = None,
|
|
435
|
-
width: Optional[int] = None,
|
|
436
|
-
padding: int = 0,
|
|
437
|
-
margin: int = 0,
|
|
438
|
-
align: str = "left",
|
|
439
|
-
|
|
440
|
-
# Border options
|
|
441
|
-
border: bool = False,
|
|
442
|
-
border_style: Optional[str] = None,
|
|
443
|
-
border_char: str = "─",
|
|
444
|
-
border_color: Optional[str] = None,
|
|
445
|
-
rounded_corners: bool = False,
|
|
446
|
-
double_border: bool = False,
|
|
447
|
-
|
|
448
|
-
# Animation options
|
|
449
|
-
animate: bool = False,
|
|
450
|
-
animation_speed: float = 0.05,
|
|
451
|
-
animation_type: str = "typing",
|
|
452
|
-
|
|
453
|
-
# Special features
|
|
454
|
-
as_table: bool = False,
|
|
455
|
-
as_tree: bool = False,
|
|
456
|
-
as_json: bool = False,
|
|
457
|
-
as_code: bool = False,
|
|
458
|
-
language: str = "python",
|
|
459
|
-
|
|
460
|
-
# Advanced features
|
|
461
|
-
raw: bool = False) -> None:
|
|
462
|
-
"""
|
|
463
|
-
Enhanced print with all builtin features plus rich formatting.
|
|
464
|
-
|
|
465
|
-
Supports all builtin print parameters plus rich formatting features.
|
|
466
|
-
Automatically detects and formats markdown content unless explicitly disabled.
|
|
467
|
-
"""
|
|
468
|
-
# Handle raw output mode
|
|
469
|
-
if raw:
|
|
470
|
-
print(*args, sep=sep, end=end, file=file or self.file, flush=flush)
|
|
471
|
-
return
|
|
472
|
-
|
|
473
|
-
# Join args with separator
|
|
474
|
-
output = sep.join(str(arg) for arg in args)
|
|
475
|
-
|
|
476
|
-
# Auto-detect markdown if not explicitly set
|
|
477
|
-
if markdown is None:
|
|
478
|
-
markdown = any(marker in output for marker in [
|
|
479
|
-
'#', '*', '_', '`', '>', '-', '•', '|',
|
|
480
|
-
# '✨', '🔥', '💫', '☐', '☑',
|
|
481
|
-
'http://', 'https://',
|
|
482
|
-
'```', '~~~',
|
|
483
|
-
'<details>', '<summary>',
|
|
484
|
-
])
|
|
485
|
-
|
|
486
|
-
# Apply markdown formatting if enabled
|
|
487
|
-
if markdown:
|
|
488
|
-
output = self._format_markdown_stream(output)
|
|
489
|
-
|
|
490
|
-
# Build style string
|
|
491
|
-
style_str = style or ""
|
|
492
|
-
if color:
|
|
493
|
-
style_str += getattr(Colors, color.upper(), "")
|
|
494
|
-
if bg_color:
|
|
495
|
-
style_str += getattr(Colors, f"BG_{bg_color.upper()}", "")
|
|
496
|
-
if bold:
|
|
497
|
-
style_str += Colors.BOLD
|
|
498
|
-
if italic:
|
|
499
|
-
style_str += Colors.ITALIC
|
|
500
|
-
if underline:
|
|
501
|
-
style_str += Colors.UNDERLINE
|
|
502
|
-
if blink:
|
|
503
|
-
style_str += Colors.BLINK
|
|
504
|
-
if strike:
|
|
505
|
-
style_str += Colors.STRIKE
|
|
506
|
-
if dim:
|
|
507
|
-
style_str += Colors.DIM
|
|
508
|
-
if reverse:
|
|
509
|
-
style_str += Colors.REVERSE
|
|
510
|
-
|
|
511
|
-
# Apply style if any
|
|
512
|
-
if style_str:
|
|
513
|
-
output = f"{style_str}{output}{Colors.RESET}"
|
|
514
|
-
|
|
515
|
-
# Handle special formatting
|
|
516
|
-
if as_json:
|
|
517
|
-
output = self._format_json(output)
|
|
518
|
-
elif as_code:
|
|
519
|
-
output = self._highlight_code(output, language)
|
|
520
|
-
elif as_table:
|
|
521
|
-
if isinstance(output, (list, tuple)):
|
|
522
|
-
self.table(*output)
|
|
523
|
-
return
|
|
524
|
-
elif as_tree:
|
|
525
|
-
if isinstance(output, (dict, list)):
|
|
526
|
-
self.tree(output)
|
|
527
|
-
return
|
|
528
|
-
|
|
529
|
-
# Apply layout options
|
|
530
|
-
if center:
|
|
531
|
-
term_width = self._get_terminal_width()
|
|
532
|
-
output = output.center(term_width)
|
|
533
|
-
if indent > 0:
|
|
534
|
-
output = " " * (indent * 4) + output
|
|
535
|
-
if prefix:
|
|
536
|
-
output = prefix + output
|
|
537
|
-
if suffix:
|
|
538
|
-
output = output + suffix
|
|
539
|
-
if width:
|
|
540
|
-
output = textwrap.fill(output, width=width)
|
|
541
|
-
|
|
542
|
-
# Add borders
|
|
543
|
-
if border:
|
|
544
|
-
width = max(len(line) for line in output.split('\n'))
|
|
545
|
-
border_top = '┌' + border_char * width + '┐'
|
|
546
|
-
border_bottom = '└' + border_char * width + '┘'
|
|
547
|
-
output = f"{border_top}\n{output}\n{border_bottom}"
|
|
548
|
-
|
|
549
|
-
# Handle animation
|
|
550
|
-
if animate:
|
|
551
|
-
for char in output:
|
|
552
|
-
print(char, end="", flush=True)
|
|
553
|
-
time.sleep(animation_speed)
|
|
554
|
-
print(end=end, flush=flush)
|
|
555
|
-
return
|
|
556
|
-
|
|
557
|
-
# Final output
|
|
558
|
-
print(output, end=end, file=file or self.file, flush=flush)
|
|
559
|
-
|
|
560
|
-
def status(self, text: str, style: Optional[str] = None):
|
|
561
|
-
"""Print a status message that can be overwritten."""
|
|
562
|
-
style = style or self.theme['info']
|
|
563
|
-
self._clear_line()
|
|
564
|
-
self._last_line = f"{style}{text}{Colors.RESET}"
|
|
565
|
-
print(self._last_line, end='\r', file=self.file, flush=True)
|
|
566
|
-
|
|
567
|
-
def banner(self, text: str, style: Optional[str] = None):
|
|
568
|
-
"""Print a fancy banner with hover info."""
|
|
569
|
-
print(f"\033]1337;Custom=id=banner:{HoverInfo.BANNER}\a", end='')
|
|
570
|
-
style = style or self.theme['special']
|
|
571
|
-
width = self._get_terminal_width() - 4
|
|
572
|
-
|
|
573
|
-
lines = textwrap.wrap(text, width=width, break_long_words=False)
|
|
574
|
-
|
|
575
|
-
print('╔' + '═' * width + '╗')
|
|
576
|
-
for line in lines:
|
|
577
|
-
padding = width - len(line)
|
|
578
|
-
print('║ ' + line + ' ' * padding + ' ║')
|
|
579
|
-
print('╚' + '═' * width + '╝')
|
|
580
|
-
print("\033]1337;Custom=id=banner:end\a", end='')
|
|
581
|
-
|
|
582
|
-
def success(self, text: str):
|
|
583
|
-
"""Print a success message with hover info."""
|
|
584
|
-
print(f"\033]1337;Custom=id=success:{HoverInfo.SUCCESS}\a", end='')
|
|
585
|
-
self.print(f"✓ {text}", style=self.theme['success'])
|
|
586
|
-
print("\033]1337;Custom=id=success:end\a", end='')
|
|
587
|
-
|
|
588
|
-
def error(self, text: str):
|
|
589
|
-
"""Print an error message with hover info."""
|
|
590
|
-
print(f"\033]1337;Custom=id=error:{HoverInfo.ERROR}\a", end='')
|
|
591
|
-
self.print(f"✗ {text}", style=self.theme['error'])
|
|
592
|
-
print("\033]1337;Custom=id=error:end\a", end='')
|
|
593
|
-
|
|
594
|
-
def warning(self, text: str):
|
|
595
|
-
"""Print a warning message with hover info."""
|
|
596
|
-
print(f"\033]1337;Custom=id=warning:{HoverInfo.WARNING}\a", end='')
|
|
597
|
-
self.print(f"⚠ {text}", style=self.theme['warning'])
|
|
598
|
-
print("\033]1337;Custom=id=warning:end\a", end='')
|
|
599
|
-
|
|
600
|
-
def info(self, text: str):
|
|
601
|
-
"""Print an info message with hover info."""
|
|
602
|
-
print(f"\033]1337;Custom=id=info:{HoverInfo.INFO}\a", end='')
|
|
603
|
-
self.print(f"ℹ {text}", style=self.theme['info'])
|
|
604
|
-
print("\033]1337;Custom=id=info:end\a", end='')
|
|
605
|
-
|
|
606
|
-
def table(self, headers: List[str], rows: List[List[Any]], style: Optional[str] = None):
|
|
607
|
-
"""Print a formatted table."""
|
|
608
|
-
style = style or self.theme['special']
|
|
609
|
-
|
|
610
|
-
col_widths = [len(h) for h in headers]
|
|
611
|
-
for row in rows:
|
|
612
|
-
for i, cell in enumerate(row):
|
|
613
|
-
col_widths[i] = max(col_widths[i], len(str(cell)))
|
|
614
|
-
|
|
615
|
-
header_line = '| ' + ' | '.join(f"{h:<{w}}" for h, w in zip(headers, col_widths)) + ' |'
|
|
616
|
-
print(header_line)
|
|
617
|
-
|
|
618
|
-
separator = '+' + '+'.join('-' * (w + 2) for w in col_widths) + '+'
|
|
619
|
-
print(separator)
|
|
620
|
-
|
|
621
|
-
for row in rows:
|
|
622
|
-
row_line = '| ' + ' | '.join(f"{str(cell):<{w}}" for cell, w in zip(row, col_widths)) + ' |'
|
|
623
|
-
print(row_line)
|
|
624
|
-
|
|
625
|
-
def tree(self, data: Union[Dict, List], indent: int = 0):
|
|
626
|
-
"""Print a tree structure of nested data."""
|
|
627
|
-
if isinstance(data, dict):
|
|
628
|
-
for key, value in data.items():
|
|
629
|
-
self.print(" " * indent + "├─ " + str(key) + ":", style=self.theme['key'])
|
|
630
|
-
if isinstance(value, (dict, list)):
|
|
631
|
-
self.tree(value, indent + 1)
|
|
632
|
-
else:
|
|
633
|
-
self.print(" " * (indent + 1) + "└─ " + str(value))
|
|
634
|
-
elif isinstance(data, list):
|
|
635
|
-
for item in data:
|
|
636
|
-
if isinstance(item, (dict, list)):
|
|
637
|
-
self.tree(item, indent + 1)
|
|
638
|
-
else:
|
|
639
|
-
self.print(" " * indent + "├─ " + str(item))
|
|
640
|
-
|
|
641
|
-
def json(self, data: Any, indent: int = 4):
|
|
642
|
-
"""Print formatted JSON data."""
|
|
643
|
-
formatted = json.dumps(data, indent=indent)
|
|
644
|
-
self.print(formatted, highlight=True)
|
|
645
|
-
|
|
646
|
-
def code_block(self, code: str, language: str = "python"):
|
|
647
|
-
"""Print code in a fancy box with syntax highlighting."""
|
|
648
|
-
highlighted = self._highlight_code(code, language)
|
|
649
|
-
lines = highlighted.split('\n')
|
|
650
|
-
|
|
651
|
-
width = max(len(line) for line in lines)
|
|
652
|
-
width = min(width, self._get_terminal_width() - 4) # Account for borders
|
|
653
|
-
|
|
654
|
-
print('┌' + '─' * width + '┐')
|
|
655
|
-
|
|
656
|
-
for line in lines:
|
|
657
|
-
if len(line) > width:
|
|
658
|
-
line = line[:width-3] + '...'
|
|
659
|
-
else:
|
|
660
|
-
line = line + ' ' * (width - len(line))
|
|
661
|
-
print('│ ' + line + ' │')
|
|
662
|
-
|
|
663
|
-
print('└' + '─' * width + '┘')
|
|
664
|
-
|
|
665
|
-
def _clear_line(self):
|
|
666
|
-
"""Clear the current line."""
|
|
667
|
-
print('\r' + ' ' * self._get_terminal_width(), end='\r', file=self.file, flush=True)
|
|
1
|
+
"""
|
|
2
|
+
>>> from litprinter import litprint
|
|
3
|
+
>>> from litprinter import lit
|
|
4
|
+
>>> from litprinter import install, uninstall
|
|
5
|
+
>>>
|
|
6
|
+
>>> litprint("Hello, world!")
|
|
7
|
+
LIT -> [__main__.py:1] in () >>> Hello, world!
|
|
8
|
+
>>>
|
|
9
|
+
>>> def my_function():
|
|
10
|
+
... lit(1, 2, 3)
|
|
11
|
+
>>> my_function()
|
|
12
|
+
LIT -> [__main__.py:4] in my_function() >>> 1, 2, 3
|
|
13
|
+
>>> install()
|
|
14
|
+
>>> ic("This is now the builtins.ic()")
|
|
15
|
+
LIT -> [__main__.py:7] in () >>> This is now the builtins.ic()
|
|
16
|
+
>>> uninstall()
|
|
17
|
+
|
|
18
|
+
This module provides enhanced print and logging functionalities for Python,
|
|
19
|
+
allowing developers to debug their code with style and precision. It
|
|
20
|
+
includes the litprint and lit functions for debugging, log for logging, and
|
|
21
|
+
install/uninstall functions for integration into the builtins module.
|
|
22
|
+
It also handles colorizing output and provides different styles and customizable
|
|
23
|
+
options.
|
|
24
|
+
|
|
25
|
+
LITPRINTER is inspired by the icecream package and provides similar functionality
|
|
26
|
+
with additional features:
|
|
27
|
+
- Variable inspection with expression display
|
|
28
|
+
- Return value handling for inline usage
|
|
29
|
+
- Support for custom formatters for specific data types
|
|
30
|
+
- Execution context tracking
|
|
31
|
+
- Rich-like colorized output with multiple themes (JARVIS, RICH, MODERN, NEON, CYBERPUNK)
|
|
32
|
+
- Better JSON formatting with indent=2 by default
|
|
33
|
+
- Advanced pretty printing for complex data structures with smart truncation
|
|
34
|
+
- Clickable file paths in supported terminals and editors (VSCode compatible)
|
|
35
|
+
- Enhanced visual formatting with better spacing and separators
|
|
36
|
+
- Special formatters for common types (Exception, bytes, set, frozenset, etc.)
|
|
37
|
+
- Smart object introspection for custom classes
|
|
38
|
+
- Logging capabilities with timestamp and log levels
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Try to import from the standalone litprinter package first
|
|
42
|
+
# If it's not installed
|
|
43
|
+
try:
|
|
44
|
+
import litprinter
|
|
45
|
+
# If standalone package is found, re-export all its components
|
|
46
|
+
from litprinter import litprint, lit, log, ic, install, uninstall
|
|
47
|
+
from litprinter import LITPrintDebugger, argumentToString
|
|
48
|
+
from litprinter import JARVIS, RICH, MODERN, NEON, CYBERPUNK, create_custom_style
|
|
49
|
+
from litprinter import traceback
|
|
50
|
+
# For compatibility with icecream
|
|
51
|
+
enable = litprinter.enable
|
|
52
|
+
disable = litprinter.disable
|
|
53
|
+
|
|
54
|
+
except ImportError:
|
|
55
|
+
# Raise a more informative error when litprinter is not installed
|
|
56
|
+
raise ImportError(
|
|
57
|
+
"The 'litprinter' package is required but not installed. "
|
|
58
|
+
"Please install it using: pip install litprinter"
|
|
59
|
+
) from None
|