webscout 8.3.7__py3-none-any.whl → 2025.10.11__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/AIauto.py +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +236 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +30 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +664 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/webscout_search.py +1183 -1183
- webscout/webscout_search_async.py +649 -649
- webscout/yep_search.py +346 -346
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/METADATA +937 -937
- webscout-2025.10.11.dist-info/RECORD +300 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/top_level.txt +0 -0
|
@@ -1,302 +1,302 @@
|
|
|
1
|
-
"""Output formatting decorators for SwiftCLI."""
|
|
2
|
-
|
|
3
|
-
from functools import wraps
|
|
4
|
-
from typing import Any, Callable, List, Optional, Union
|
|
5
|
-
|
|
6
|
-
from rich.console import Console
|
|
7
|
-
from rich.table import Table
|
|
8
|
-
from rich.panel import Panel
|
|
9
|
-
|
|
10
|
-
# Handle different versions of rich
|
|
11
|
-
try:
|
|
12
|
-
from rich.progress import (
|
|
13
|
-
Progress,
|
|
14
|
-
SpinnerColumn,
|
|
15
|
-
TextColumn,
|
|
16
|
-
BarColumn,
|
|
17
|
-
TaskProgressColumn,
|
|
18
|
-
TimeRemainingColumn
|
|
19
|
-
)
|
|
20
|
-
except ImportError:
|
|
21
|
-
# Fallback for older versions of rich
|
|
22
|
-
try:
|
|
23
|
-
from rich.progress import (
|
|
24
|
-
Progress,
|
|
25
|
-
SpinnerColumn,
|
|
26
|
-
TextColumn,
|
|
27
|
-
BarColumn,
|
|
28
|
-
TimeRemainingColumn
|
|
29
|
-
)
|
|
30
|
-
# Create a simple TaskProgressColumn replacement for older versions
|
|
31
|
-
class TaskProgressColumn:
|
|
32
|
-
def __init__(self):
|
|
33
|
-
pass
|
|
34
|
-
|
|
35
|
-
def __call__(self, task):
|
|
36
|
-
return f"{task.percentage:.1f}%"
|
|
37
|
-
|
|
38
|
-
except ImportError:
|
|
39
|
-
# If rich is too old, create minimal fallbacks
|
|
40
|
-
class Progress:
|
|
41
|
-
def __init__(self, *args, **kwargs):
|
|
42
|
-
pass
|
|
43
|
-
def __enter__(self):
|
|
44
|
-
return self
|
|
45
|
-
def __exit__(self, *args):
|
|
46
|
-
pass
|
|
47
|
-
def add_task(self, description, total=None):
|
|
48
|
-
return 0
|
|
49
|
-
def update(self, task_id, **kwargs):
|
|
50
|
-
pass
|
|
51
|
-
|
|
52
|
-
class SpinnerColumn:
|
|
53
|
-
pass
|
|
54
|
-
class TextColumn:
|
|
55
|
-
def __init__(self, text):
|
|
56
|
-
pass
|
|
57
|
-
class BarColumn:
|
|
58
|
-
pass
|
|
59
|
-
class TaskProgressColumn:
|
|
60
|
-
pass
|
|
61
|
-
class TimeRemainingColumn:
|
|
62
|
-
pass
|
|
63
|
-
|
|
64
|
-
console = Console()
|
|
65
|
-
|
|
66
|
-
def table_output(
|
|
67
|
-
headers: List[str],
|
|
68
|
-
title: Optional[str] = None,
|
|
69
|
-
style: str = "default",
|
|
70
|
-
show_lines: bool = False,
|
|
71
|
-
expand: bool = False
|
|
72
|
-
) -> Callable:
|
|
73
|
-
"""
|
|
74
|
-
Decorator to format command output as a table.
|
|
75
|
-
|
|
76
|
-
Args:
|
|
77
|
-
headers: Column headers
|
|
78
|
-
title: Table title
|
|
79
|
-
style: Table style
|
|
80
|
-
show_lines: Show row/column lines
|
|
81
|
-
expand: Expand table to terminal width
|
|
82
|
-
|
|
83
|
-
Example:
|
|
84
|
-
@command()
|
|
85
|
-
@table_output(["ID", "Name", "Status"])
|
|
86
|
-
def list_users():
|
|
87
|
-
'''List all users'''
|
|
88
|
-
return [
|
|
89
|
-
[1, "John", "Active"],
|
|
90
|
-
[2, "Jane", "Inactive"]
|
|
91
|
-
]
|
|
92
|
-
"""
|
|
93
|
-
def decorator(f: Callable) -> Callable:
|
|
94
|
-
@wraps(f)
|
|
95
|
-
def wrapper(*args, **kwargs):
|
|
96
|
-
result = f(*args, **kwargs)
|
|
97
|
-
if result:
|
|
98
|
-
table = Table(
|
|
99
|
-
title=title,
|
|
100
|
-
show_header=True,
|
|
101
|
-
header_style="bold blue",
|
|
102
|
-
show_lines=show_lines,
|
|
103
|
-
expand=expand
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
# Add columns
|
|
107
|
-
for header in headers:
|
|
108
|
-
table.add_column(header)
|
|
109
|
-
|
|
110
|
-
# Add rows
|
|
111
|
-
for row in result:
|
|
112
|
-
table.add_row(*[str(cell) for cell in row])
|
|
113
|
-
|
|
114
|
-
console.print(table)
|
|
115
|
-
return result
|
|
116
|
-
return wrapper
|
|
117
|
-
return decorator
|
|
118
|
-
|
|
119
|
-
def progress(
|
|
120
|
-
description: str = None,
|
|
121
|
-
total: Optional[int] = None,
|
|
122
|
-
transient: bool = False,
|
|
123
|
-
show_bar: bool = True,
|
|
124
|
-
show_percentage: bool = True,
|
|
125
|
-
show_time: bool = True
|
|
126
|
-
) -> Callable:
|
|
127
|
-
"""
|
|
128
|
-
Decorator to show progress for long-running commands.
|
|
129
|
-
|
|
130
|
-
The decorated function should be a generator that yields
|
|
131
|
-
progress updates.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
description: Task description
|
|
135
|
-
total: Total number of steps
|
|
136
|
-
transient: Remove progress when done
|
|
137
|
-
show_bar: Show progress bar
|
|
138
|
-
show_percentage: Show percentage complete
|
|
139
|
-
show_time: Show time remaining
|
|
140
|
-
|
|
141
|
-
Example:
|
|
142
|
-
@command()
|
|
143
|
-
@progress("Processing files")
|
|
144
|
-
def process(files: List[str]):
|
|
145
|
-
'''Process multiple files'''
|
|
146
|
-
for file in files:
|
|
147
|
-
# Process file
|
|
148
|
-
yield f"Processing {file}..."
|
|
149
|
-
"""
|
|
150
|
-
def decorator(f: Callable) -> Callable:
|
|
151
|
-
@wraps(f)
|
|
152
|
-
def wrapper(*args, **kwargs):
|
|
153
|
-
columns = []
|
|
154
|
-
columns.append(SpinnerColumn())
|
|
155
|
-
columns.append(TextColumn("[progress.description]{task.description}"))
|
|
156
|
-
|
|
157
|
-
if show_bar:
|
|
158
|
-
columns.append(BarColumn())
|
|
159
|
-
if show_percentage:
|
|
160
|
-
try:
|
|
161
|
-
columns.append(TaskProgressColumn())
|
|
162
|
-
except:
|
|
163
|
-
# Fallback for older rich versions
|
|
164
|
-
columns.append(TextColumn("[progress.percentage]{task.percentage:>3.0f}%"))
|
|
165
|
-
if show_time:
|
|
166
|
-
columns.append(TimeRemainingColumn())
|
|
167
|
-
|
|
168
|
-
with Progress(*columns, transient=transient) as progress:
|
|
169
|
-
task = progress.add_task(
|
|
170
|
-
description or f.__name__,
|
|
171
|
-
total=total
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
try:
|
|
175
|
-
for update in f(*args, **kwargs):
|
|
176
|
-
if isinstance(update, (int, float)):
|
|
177
|
-
# Update progress by number
|
|
178
|
-
progress.update(task, advance=update)
|
|
179
|
-
elif isinstance(update, str):
|
|
180
|
-
# Update description
|
|
181
|
-
progress.update(task, description=update)
|
|
182
|
-
elif isinstance(update, dict):
|
|
183
|
-
# Update multiple attributes
|
|
184
|
-
progress.update(task, **update)
|
|
185
|
-
else:
|
|
186
|
-
# Just advance by 1
|
|
187
|
-
progress.update(task, advance=1)
|
|
188
|
-
except Exception as e:
|
|
189
|
-
progress.update(task, description=f"Error: {str(e)}")
|
|
190
|
-
raise
|
|
191
|
-
finally:
|
|
192
|
-
progress.update(task, completed=total or 100)
|
|
193
|
-
|
|
194
|
-
return wrapper
|
|
195
|
-
return decorator
|
|
196
|
-
|
|
197
|
-
def panel_output(
|
|
198
|
-
title: Optional[str] = None,
|
|
199
|
-
style: str = "default",
|
|
200
|
-
expand: bool = True,
|
|
201
|
-
padding: Union[int, tuple] = 1
|
|
202
|
-
) -> Callable:
|
|
203
|
-
"""
|
|
204
|
-
Decorator to display command output in a panel.
|
|
205
|
-
|
|
206
|
-
Args:
|
|
207
|
-
title: Panel title
|
|
208
|
-
style: Panel style
|
|
209
|
-
expand: Expand panel to terminal width
|
|
210
|
-
padding: Panel padding
|
|
211
|
-
|
|
212
|
-
Example:
|
|
213
|
-
@command()
|
|
214
|
-
@panel_output(title="System Status")
|
|
215
|
-
def status():
|
|
216
|
-
'''Show system status'''
|
|
217
|
-
return "All systems operational"
|
|
218
|
-
"""
|
|
219
|
-
def decorator(f: Callable) -> Callable:
|
|
220
|
-
@wraps(f)
|
|
221
|
-
def wrapper(*args, **kwargs):
|
|
222
|
-
result = f(*args, **kwargs)
|
|
223
|
-
if result:
|
|
224
|
-
panel = Panel(
|
|
225
|
-
str(result),
|
|
226
|
-
title=title,
|
|
227
|
-
style=style,
|
|
228
|
-
expand=expand,
|
|
229
|
-
padding=padding
|
|
230
|
-
)
|
|
231
|
-
console.print(panel)
|
|
232
|
-
return result
|
|
233
|
-
return wrapper
|
|
234
|
-
return decorator
|
|
235
|
-
|
|
236
|
-
def format_output(
|
|
237
|
-
template: str,
|
|
238
|
-
style: Optional[str] = None
|
|
239
|
-
) -> Callable:
|
|
240
|
-
"""
|
|
241
|
-
Decorator to format command output using a template.
|
|
242
|
-
|
|
243
|
-
Args:
|
|
244
|
-
template: Format string template
|
|
245
|
-
style: Rich style string
|
|
246
|
-
|
|
247
|
-
Example:
|
|
248
|
-
@command()
|
|
249
|
-
@format_output("Created user {name} with ID {id}")
|
|
250
|
-
def create_user(name: str):
|
|
251
|
-
'''Create a new user'''
|
|
252
|
-
return {"name": name, "id": 123}
|
|
253
|
-
"""
|
|
254
|
-
def decorator(f: Callable) -> Callable:
|
|
255
|
-
@wraps(f)
|
|
256
|
-
def wrapper(*args, **kwargs):
|
|
257
|
-
result = f(*args, **kwargs)
|
|
258
|
-
if result:
|
|
259
|
-
if isinstance(result, dict):
|
|
260
|
-
output = template.format(**result)
|
|
261
|
-
else:
|
|
262
|
-
output = template.format(result)
|
|
263
|
-
|
|
264
|
-
if style:
|
|
265
|
-
console.print(output, style=style)
|
|
266
|
-
else:
|
|
267
|
-
console.print(output)
|
|
268
|
-
return result
|
|
269
|
-
return wrapper
|
|
270
|
-
return decorator
|
|
271
|
-
|
|
272
|
-
def pager_output(
|
|
273
|
-
use_pager: bool = True,
|
|
274
|
-
style: Optional[str] = None
|
|
275
|
-
) -> Callable:
|
|
276
|
-
"""
|
|
277
|
-
Decorator to display command output in a pager.
|
|
278
|
-
|
|
279
|
-
Args:
|
|
280
|
-
use_pager: Whether to use pager
|
|
281
|
-
style: Rich style string
|
|
282
|
-
|
|
283
|
-
Example:
|
|
284
|
-
@command()
|
|
285
|
-
@pager_output()
|
|
286
|
-
def show_logs():
|
|
287
|
-
'''Show application logs'''
|
|
288
|
-
return "Very long log output..."
|
|
289
|
-
"""
|
|
290
|
-
def decorator(f: Callable) -> Callable:
|
|
291
|
-
@wraps(f)
|
|
292
|
-
def wrapper(*args, **kwargs):
|
|
293
|
-
result = f(*args, **kwargs)
|
|
294
|
-
if result:
|
|
295
|
-
with console.pager(styles=use_pager):
|
|
296
|
-
if style:
|
|
297
|
-
console.print(result, style=style)
|
|
298
|
-
else:
|
|
299
|
-
console.print(result)
|
|
300
|
-
return result
|
|
301
|
-
return wrapper
|
|
302
|
-
return decorator
|
|
1
|
+
"""Output formatting decorators for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import Any, Callable, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
|
|
10
|
+
# Handle different versions of rich
|
|
11
|
+
try:
|
|
12
|
+
from rich.progress import (
|
|
13
|
+
Progress,
|
|
14
|
+
SpinnerColumn,
|
|
15
|
+
TextColumn,
|
|
16
|
+
BarColumn,
|
|
17
|
+
TaskProgressColumn,
|
|
18
|
+
TimeRemainingColumn
|
|
19
|
+
)
|
|
20
|
+
except ImportError:
|
|
21
|
+
# Fallback for older versions of rich
|
|
22
|
+
try:
|
|
23
|
+
from rich.progress import (
|
|
24
|
+
Progress,
|
|
25
|
+
SpinnerColumn,
|
|
26
|
+
TextColumn,
|
|
27
|
+
BarColumn,
|
|
28
|
+
TimeRemainingColumn
|
|
29
|
+
)
|
|
30
|
+
# Create a simple TaskProgressColumn replacement for older versions
|
|
31
|
+
class TaskProgressColumn:
|
|
32
|
+
def __init__(self):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def __call__(self, task):
|
|
36
|
+
return f"{task.percentage:.1f}%"
|
|
37
|
+
|
|
38
|
+
except ImportError:
|
|
39
|
+
# If rich is too old, create minimal fallbacks
|
|
40
|
+
class Progress:
|
|
41
|
+
def __init__(self, *args, **kwargs):
|
|
42
|
+
pass
|
|
43
|
+
def __enter__(self):
|
|
44
|
+
return self
|
|
45
|
+
def __exit__(self, *args):
|
|
46
|
+
pass
|
|
47
|
+
def add_task(self, description, total=None):
|
|
48
|
+
return 0
|
|
49
|
+
def update(self, task_id, **kwargs):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
class SpinnerColumn:
|
|
53
|
+
pass
|
|
54
|
+
class TextColumn:
|
|
55
|
+
def __init__(self, text):
|
|
56
|
+
pass
|
|
57
|
+
class BarColumn:
|
|
58
|
+
pass
|
|
59
|
+
class TaskProgressColumn:
|
|
60
|
+
pass
|
|
61
|
+
class TimeRemainingColumn:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
console = Console()
|
|
65
|
+
|
|
66
|
+
def table_output(
|
|
67
|
+
headers: List[str],
|
|
68
|
+
title: Optional[str] = None,
|
|
69
|
+
style: str = "default",
|
|
70
|
+
show_lines: bool = False,
|
|
71
|
+
expand: bool = False
|
|
72
|
+
) -> Callable:
|
|
73
|
+
"""
|
|
74
|
+
Decorator to format command output as a table.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
headers: Column headers
|
|
78
|
+
title: Table title
|
|
79
|
+
style: Table style
|
|
80
|
+
show_lines: Show row/column lines
|
|
81
|
+
expand: Expand table to terminal width
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
@command()
|
|
85
|
+
@table_output(["ID", "Name", "Status"])
|
|
86
|
+
def list_users():
|
|
87
|
+
'''List all users'''
|
|
88
|
+
return [
|
|
89
|
+
[1, "John", "Active"],
|
|
90
|
+
[2, "Jane", "Inactive"]
|
|
91
|
+
]
|
|
92
|
+
"""
|
|
93
|
+
def decorator(f: Callable) -> Callable:
|
|
94
|
+
@wraps(f)
|
|
95
|
+
def wrapper(*args, **kwargs):
|
|
96
|
+
result = f(*args, **kwargs)
|
|
97
|
+
if result:
|
|
98
|
+
table = Table(
|
|
99
|
+
title=title,
|
|
100
|
+
show_header=True,
|
|
101
|
+
header_style="bold blue",
|
|
102
|
+
show_lines=show_lines,
|
|
103
|
+
expand=expand
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Add columns
|
|
107
|
+
for header in headers:
|
|
108
|
+
table.add_column(header)
|
|
109
|
+
|
|
110
|
+
# Add rows
|
|
111
|
+
for row in result:
|
|
112
|
+
table.add_row(*[str(cell) for cell in row])
|
|
113
|
+
|
|
114
|
+
console.print(table)
|
|
115
|
+
return result
|
|
116
|
+
return wrapper
|
|
117
|
+
return decorator
|
|
118
|
+
|
|
119
|
+
def progress(
|
|
120
|
+
description: str = None,
|
|
121
|
+
total: Optional[int] = None,
|
|
122
|
+
transient: bool = False,
|
|
123
|
+
show_bar: bool = True,
|
|
124
|
+
show_percentage: bool = True,
|
|
125
|
+
show_time: bool = True
|
|
126
|
+
) -> Callable:
|
|
127
|
+
"""
|
|
128
|
+
Decorator to show progress for long-running commands.
|
|
129
|
+
|
|
130
|
+
The decorated function should be a generator that yields
|
|
131
|
+
progress updates.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
description: Task description
|
|
135
|
+
total: Total number of steps
|
|
136
|
+
transient: Remove progress when done
|
|
137
|
+
show_bar: Show progress bar
|
|
138
|
+
show_percentage: Show percentage complete
|
|
139
|
+
show_time: Show time remaining
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
@command()
|
|
143
|
+
@progress("Processing files")
|
|
144
|
+
def process(files: List[str]):
|
|
145
|
+
'''Process multiple files'''
|
|
146
|
+
for file in files:
|
|
147
|
+
# Process file
|
|
148
|
+
yield f"Processing {file}..."
|
|
149
|
+
"""
|
|
150
|
+
def decorator(f: Callable) -> Callable:
|
|
151
|
+
@wraps(f)
|
|
152
|
+
def wrapper(*args, **kwargs):
|
|
153
|
+
columns = []
|
|
154
|
+
columns.append(SpinnerColumn())
|
|
155
|
+
columns.append(TextColumn("[progress.description]{task.description}"))
|
|
156
|
+
|
|
157
|
+
if show_bar:
|
|
158
|
+
columns.append(BarColumn())
|
|
159
|
+
if show_percentage:
|
|
160
|
+
try:
|
|
161
|
+
columns.append(TaskProgressColumn())
|
|
162
|
+
except:
|
|
163
|
+
# Fallback for older rich versions
|
|
164
|
+
columns.append(TextColumn("[progress.percentage]{task.percentage:>3.0f}%"))
|
|
165
|
+
if show_time:
|
|
166
|
+
columns.append(TimeRemainingColumn())
|
|
167
|
+
|
|
168
|
+
with Progress(*columns, transient=transient) as progress:
|
|
169
|
+
task = progress.add_task(
|
|
170
|
+
description or f.__name__,
|
|
171
|
+
total=total
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
for update in f(*args, **kwargs):
|
|
176
|
+
if isinstance(update, (int, float)):
|
|
177
|
+
# Update progress by number
|
|
178
|
+
progress.update(task, advance=update)
|
|
179
|
+
elif isinstance(update, str):
|
|
180
|
+
# Update description
|
|
181
|
+
progress.update(task, description=update)
|
|
182
|
+
elif isinstance(update, dict):
|
|
183
|
+
# Update multiple attributes
|
|
184
|
+
progress.update(task, **update)
|
|
185
|
+
else:
|
|
186
|
+
# Just advance by 1
|
|
187
|
+
progress.update(task, advance=1)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
progress.update(task, description=f"Error: {str(e)}")
|
|
190
|
+
raise
|
|
191
|
+
finally:
|
|
192
|
+
progress.update(task, completed=total or 100)
|
|
193
|
+
|
|
194
|
+
return wrapper
|
|
195
|
+
return decorator
|
|
196
|
+
|
|
197
|
+
def panel_output(
|
|
198
|
+
title: Optional[str] = None,
|
|
199
|
+
style: str = "default",
|
|
200
|
+
expand: bool = True,
|
|
201
|
+
padding: Union[int, tuple] = 1
|
|
202
|
+
) -> Callable:
|
|
203
|
+
"""
|
|
204
|
+
Decorator to display command output in a panel.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
title: Panel title
|
|
208
|
+
style: Panel style
|
|
209
|
+
expand: Expand panel to terminal width
|
|
210
|
+
padding: Panel padding
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
@command()
|
|
214
|
+
@panel_output(title="System Status")
|
|
215
|
+
def status():
|
|
216
|
+
'''Show system status'''
|
|
217
|
+
return "All systems operational"
|
|
218
|
+
"""
|
|
219
|
+
def decorator(f: Callable) -> Callable:
|
|
220
|
+
@wraps(f)
|
|
221
|
+
def wrapper(*args, **kwargs):
|
|
222
|
+
result = f(*args, **kwargs)
|
|
223
|
+
if result:
|
|
224
|
+
panel = Panel(
|
|
225
|
+
str(result),
|
|
226
|
+
title=title,
|
|
227
|
+
style=style,
|
|
228
|
+
expand=expand,
|
|
229
|
+
padding=padding
|
|
230
|
+
)
|
|
231
|
+
console.print(panel)
|
|
232
|
+
return result
|
|
233
|
+
return wrapper
|
|
234
|
+
return decorator
|
|
235
|
+
|
|
236
|
+
def format_output(
|
|
237
|
+
template: str,
|
|
238
|
+
style: Optional[str] = None
|
|
239
|
+
) -> Callable:
|
|
240
|
+
"""
|
|
241
|
+
Decorator to format command output using a template.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
template: Format string template
|
|
245
|
+
style: Rich style string
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
@command()
|
|
249
|
+
@format_output("Created user {name} with ID {id}")
|
|
250
|
+
def create_user(name: str):
|
|
251
|
+
'''Create a new user'''
|
|
252
|
+
return {"name": name, "id": 123}
|
|
253
|
+
"""
|
|
254
|
+
def decorator(f: Callable) -> Callable:
|
|
255
|
+
@wraps(f)
|
|
256
|
+
def wrapper(*args, **kwargs):
|
|
257
|
+
result = f(*args, **kwargs)
|
|
258
|
+
if result:
|
|
259
|
+
if isinstance(result, dict):
|
|
260
|
+
output = template.format(**result)
|
|
261
|
+
else:
|
|
262
|
+
output = template.format(result)
|
|
263
|
+
|
|
264
|
+
if style:
|
|
265
|
+
console.print(output, style=style)
|
|
266
|
+
else:
|
|
267
|
+
console.print(output)
|
|
268
|
+
return result
|
|
269
|
+
return wrapper
|
|
270
|
+
return decorator
|
|
271
|
+
|
|
272
|
+
def pager_output(
|
|
273
|
+
use_pager: bool = True,
|
|
274
|
+
style: Optional[str] = None
|
|
275
|
+
) -> Callable:
|
|
276
|
+
"""
|
|
277
|
+
Decorator to display command output in a pager.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
use_pager: Whether to use pager
|
|
281
|
+
style: Rich style string
|
|
282
|
+
|
|
283
|
+
Example:
|
|
284
|
+
@command()
|
|
285
|
+
@pager_output()
|
|
286
|
+
def show_logs():
|
|
287
|
+
'''Show application logs'''
|
|
288
|
+
return "Very long log output..."
|
|
289
|
+
"""
|
|
290
|
+
def decorator(f: Callable) -> Callable:
|
|
291
|
+
@wraps(f)
|
|
292
|
+
def wrapper(*args, **kwargs):
|
|
293
|
+
result = f(*args, **kwargs)
|
|
294
|
+
if result:
|
|
295
|
+
with console.pager(styles=use_pager):
|
|
296
|
+
if style:
|
|
297
|
+
console.print(result, style=style)
|
|
298
|
+
else:
|
|
299
|
+
console.print(result)
|
|
300
|
+
return result
|
|
301
|
+
return wrapper
|
|
302
|
+
return decorator
|
webscout/swiftcli/exceptions.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
"""Exception classes for SwiftCLI."""
|
|
2
|
-
|
|
3
|
-
class SwiftCLIException(Exception):
|
|
4
|
-
"""Base exception class for SwiftCLI."""
|
|
5
|
-
pass
|
|
6
|
-
|
|
7
|
-
class UsageError(SwiftCLIException):
|
|
8
|
-
"""Raised when CLI is used incorrectly."""
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
class BadParameter(UsageError):
|
|
12
|
-
"""Raised when a parameter is invalid."""
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
class ConfigError(SwiftCLIException):
|
|
16
|
-
"""Raised when there is a configuration error."""
|
|
17
|
-
pass
|
|
18
|
-
|
|
19
|
-
class PluginError(SwiftCLIException):
|
|
20
|
-
"""Raised when there is a plugin error."""
|
|
21
|
-
pass
|
|
1
|
+
"""Exception classes for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
class SwiftCLIException(Exception):
|
|
4
|
+
"""Base exception class for SwiftCLI."""
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
class UsageError(SwiftCLIException):
|
|
8
|
+
"""Raised when CLI is used incorrectly."""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
class BadParameter(UsageError):
|
|
12
|
+
"""Raised when a parameter is invalid."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
class ConfigError(SwiftCLIException):
|
|
16
|
+
"""Raised when there is a configuration error."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
class PluginError(SwiftCLIException):
|
|
20
|
+
"""Raised when there is a plugin error."""
|
|
21
|
+
pass
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"""Plugin system for SwiftCLI."""
|
|
2
|
-
|
|
3
|
-
from .base import Plugin
|
|
4
|
-
from .manager import PluginManager
|
|
5
|
-
|
|
6
|
-
__all__ = [
|
|
7
|
-
'Plugin',
|
|
8
|
-
'PluginManager'
|
|
9
|
-
]
|
|
1
|
+
"""Plugin system for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from .base import Plugin
|
|
4
|
+
from .manager import PluginManager
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
'Plugin',
|
|
8
|
+
'PluginManager'
|
|
9
|
+
]
|