webscout 8.2.4__py3-none-any.whl → 8.2.6__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 +112 -22
- webscout/AIutel.py +240 -344
- webscout/Extra/autocoder/autocoder.py +66 -5
- webscout/Extra/gguf.py +2 -0
- webscout/Provider/AISEARCH/scira_search.py +3 -5
- webscout/Provider/Aitopia.py +75 -51
- webscout/Provider/AllenAI.py +64 -67
- webscout/Provider/ChatGPTClone.py +33 -34
- webscout/Provider/ChatSandbox.py +342 -0
- webscout/Provider/Cloudflare.py +79 -32
- webscout/Provider/Deepinfra.py +69 -56
- webscout/Provider/ElectronHub.py +48 -39
- webscout/Provider/ExaChat.py +36 -20
- webscout/Provider/GPTWeb.py +24 -18
- webscout/Provider/GithubChat.py +52 -49
- webscout/Provider/GizAI.py +285 -0
- webscout/Provider/Glider.py +39 -28
- webscout/Provider/Groq.py +48 -20
- webscout/Provider/HeckAI.py +18 -36
- webscout/Provider/Jadve.py +30 -37
- webscout/Provider/LambdaChat.py +36 -59
- webscout/Provider/MCPCore.py +18 -21
- webscout/Provider/Marcus.py +23 -14
- webscout/Provider/Nemotron.py +218 -0
- webscout/Provider/Netwrck.py +35 -26
- webscout/Provider/OPENAI/__init__.py +1 -1
- webscout/Provider/OPENAI/exachat.py +4 -0
- webscout/Provider/OPENAI/scirachat.py +3 -4
- webscout/Provider/OPENAI/textpollinations.py +20 -22
- webscout/Provider/OPENAI/toolbaz.py +1 -0
- webscout/Provider/PI.py +22 -13
- webscout/Provider/StandardInput.py +42 -30
- webscout/Provider/TeachAnything.py +24 -12
- webscout/Provider/TextPollinationsAI.py +78 -76
- webscout/Provider/TwoAI.py +120 -88
- webscout/Provider/TypliAI.py +305 -0
- webscout/Provider/Venice.py +24 -22
- webscout/Provider/VercelAI.py +31 -12
- webscout/Provider/WiseCat.py +1 -1
- webscout/Provider/WrDoChat.py +370 -0
- webscout/Provider/__init__.py +11 -13
- webscout/Provider/ai4chat.py +5 -3
- webscout/Provider/akashgpt.py +59 -66
- webscout/Provider/asksteve.py +53 -44
- webscout/Provider/cerebras.py +77 -31
- webscout/Provider/chatglm.py +47 -37
- webscout/Provider/elmo.py +38 -32
- webscout/Provider/freeaichat.py +57 -43
- webscout/Provider/granite.py +24 -21
- webscout/Provider/hermes.py +27 -20
- webscout/Provider/learnfastai.py +25 -20
- webscout/Provider/llmchatco.py +48 -78
- webscout/Provider/multichat.py +13 -3
- webscout/Provider/scira_chat.py +50 -30
- webscout/Provider/scnet.py +27 -21
- webscout/Provider/searchchat.py +16 -24
- webscout/Provider/sonus.py +37 -39
- webscout/Provider/toolbaz.py +24 -46
- webscout/Provider/turboseek.py +37 -41
- webscout/Provider/typefully.py +30 -22
- webscout/Provider/typegpt.py +47 -51
- webscout/Provider/uncovr.py +46 -40
- webscout/__init__.py +0 -1
- webscout/cli.py +256 -0
- webscout/conversation.py +305 -448
- webscout/exceptions.py +3 -0
- webscout/swiftcli/__init__.py +80 -794
- webscout/swiftcli/core/__init__.py +7 -0
- webscout/swiftcli/core/cli.py +297 -0
- webscout/swiftcli/core/context.py +104 -0
- webscout/swiftcli/core/group.py +241 -0
- webscout/swiftcli/decorators/__init__.py +28 -0
- webscout/swiftcli/decorators/command.py +221 -0
- webscout/swiftcli/decorators/options.py +220 -0
- webscout/swiftcli/decorators/output.py +252 -0
- webscout/swiftcli/exceptions.py +21 -0
- webscout/swiftcli/plugins/__init__.py +9 -0
- webscout/swiftcli/plugins/base.py +135 -0
- webscout/swiftcli/plugins/manager.py +262 -0
- webscout/swiftcli/utils/__init__.py +59 -0
- webscout/swiftcli/utils/formatting.py +252 -0
- webscout/swiftcli/utils/parsing.py +267 -0
- webscout/version.py +1 -1
- {webscout-8.2.4.dist-info → webscout-8.2.6.dist-info}/METADATA +166 -45
- {webscout-8.2.4.dist-info → webscout-8.2.6.dist-info}/RECORD +89 -89
- {webscout-8.2.4.dist-info → webscout-8.2.6.dist-info}/WHEEL +1 -1
- webscout-8.2.6.dist-info/entry_points.txt +3 -0
- {webscout-8.2.4.dist-info → webscout-8.2.6.dist-info}/top_level.txt +0 -1
- inferno/__init__.py +0 -6
- inferno/__main__.py +0 -9
- inferno/cli.py +0 -6
- inferno/lol.py +0 -589
- webscout/LLM.py +0 -442
- webscout/Local/__init__.py +0 -12
- webscout/Local/__main__.py +0 -9
- webscout/Local/api.py +0 -576
- webscout/Local/cli.py +0 -516
- webscout/Local/config.py +0 -75
- webscout/Local/llm.py +0 -287
- webscout/Local/model_manager.py +0 -253
- webscout/Local/server.py +0 -721
- webscout/Local/utils.py +0 -93
- webscout/Provider/Chatify.py +0 -175
- webscout/Provider/PizzaGPT.py +0 -228
- webscout/Provider/askmyai.py +0 -158
- webscout/Provider/gaurish.py +0 -244
- webscout/Provider/promptrefine.py +0 -193
- webscout/Provider/tutorai.py +0 -270
- webscout-8.2.4.dist-info/entry_points.txt +0 -5
- {webscout-8.2.4.dist-info → webscout-8.2.6.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""Command decorators for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from ..core.context import Context
|
|
7
|
+
|
|
8
|
+
def command(
|
|
9
|
+
name: str = None,
|
|
10
|
+
help: str = None,
|
|
11
|
+
aliases: List[str] = None,
|
|
12
|
+
hidden: bool = False
|
|
13
|
+
) -> Callable:
|
|
14
|
+
"""
|
|
15
|
+
Decorator to register a new command.
|
|
16
|
+
|
|
17
|
+
This decorator marks a function as a CLI command and provides metadata
|
|
18
|
+
about how the command should be registered and displayed.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
name: Command name (defaults to function name)
|
|
22
|
+
help: Help text (defaults to function docstring)
|
|
23
|
+
aliases: Alternative names for the command
|
|
24
|
+
hidden: Whether to hide from help output
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
@command(name="greet", help="Say hello")
|
|
28
|
+
def hello(name: str):
|
|
29
|
+
print(f"Hello {name}!")
|
|
30
|
+
|
|
31
|
+
@command(aliases=["hi", "hey"])
|
|
32
|
+
def hello(name: str):
|
|
33
|
+
'''Say hello to someone'''
|
|
34
|
+
print(f"Hello {name}!")
|
|
35
|
+
"""
|
|
36
|
+
def decorator(f: Callable) -> Callable:
|
|
37
|
+
f._command = {
|
|
38
|
+
'name': name or f.__name__,
|
|
39
|
+
'help': help or f.__doc__,
|
|
40
|
+
'aliases': aliases or [],
|
|
41
|
+
'hidden': hidden
|
|
42
|
+
}
|
|
43
|
+
return f
|
|
44
|
+
return decorator
|
|
45
|
+
|
|
46
|
+
def group(
|
|
47
|
+
name: str = None,
|
|
48
|
+
help: str = None,
|
|
49
|
+
chain: bool = False,
|
|
50
|
+
invoke_without_command: bool = False
|
|
51
|
+
) -> Callable:
|
|
52
|
+
"""
|
|
53
|
+
Decorator to create a command group.
|
|
54
|
+
|
|
55
|
+
Command groups can contain subcommands and optionally chain their results.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
name: Group name (defaults to function name)
|
|
59
|
+
help: Help text (defaults to function docstring)
|
|
60
|
+
chain: Whether to chain command results
|
|
61
|
+
invoke_without_command: Allow group to be invoked without subcommand
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
@group()
|
|
65
|
+
def db():
|
|
66
|
+
'''Database commands'''
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
@db.command()
|
|
70
|
+
def migrate():
|
|
71
|
+
'''Run database migrations'''
|
|
72
|
+
print("Running migrations...")
|
|
73
|
+
|
|
74
|
+
@group(chain=True)
|
|
75
|
+
def process():
|
|
76
|
+
'''Process data'''
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
@process.command()
|
|
80
|
+
def validate():
|
|
81
|
+
'''Validate data'''
|
|
82
|
+
return {"valid": True}
|
|
83
|
+
"""
|
|
84
|
+
def decorator(f: Callable) -> Callable:
|
|
85
|
+
f._group = {
|
|
86
|
+
'name': name or f.__name__,
|
|
87
|
+
'help': help or f.__doc__,
|
|
88
|
+
'chain': chain,
|
|
89
|
+
'invoke_without_command': invoke_without_command
|
|
90
|
+
}
|
|
91
|
+
return f
|
|
92
|
+
return decorator
|
|
93
|
+
|
|
94
|
+
def pass_context(f: Callable) -> Callable:
|
|
95
|
+
"""
|
|
96
|
+
Decorator to pass CLI context to command.
|
|
97
|
+
|
|
98
|
+
This decorator injects the current Context object as the first argument
|
|
99
|
+
to the decorated command function.
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
@command()
|
|
103
|
+
@pass_context
|
|
104
|
+
def status(ctx):
|
|
105
|
+
'''Show application status'''
|
|
106
|
+
print(f"App: {ctx.cli.name}")
|
|
107
|
+
print(f"Debug: {ctx.debug}")
|
|
108
|
+
"""
|
|
109
|
+
f._pass_context = True
|
|
110
|
+
return f
|
|
111
|
+
|
|
112
|
+
def completion(func: Optional[Callable] = None) -> Callable:
|
|
113
|
+
"""
|
|
114
|
+
Decorator to provide shell completion for a command.
|
|
115
|
+
|
|
116
|
+
The decorated function should return a list of possible completions
|
|
117
|
+
based on the current incomplete value.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
@command()
|
|
121
|
+
@option("--service", type=str)
|
|
122
|
+
def restart(service: str):
|
|
123
|
+
'''Restart a service'''
|
|
124
|
+
print(f"Restarting {service}...")
|
|
125
|
+
|
|
126
|
+
@restart.completion()
|
|
127
|
+
def complete_service(ctx, incomplete):
|
|
128
|
+
services = ["nginx", "apache", "mysql"]
|
|
129
|
+
return [s for s in services if s.startswith(incomplete)]
|
|
130
|
+
"""
|
|
131
|
+
def decorator(f: Callable) -> Callable:
|
|
132
|
+
@wraps(f)
|
|
133
|
+
def wrapper(ctx: Context, incomplete: str) -> List[str]:
|
|
134
|
+
try:
|
|
135
|
+
return f(ctx, incomplete)
|
|
136
|
+
except Exception:
|
|
137
|
+
return []
|
|
138
|
+
return wrapper
|
|
139
|
+
|
|
140
|
+
if func is None:
|
|
141
|
+
return decorator
|
|
142
|
+
return decorator(func)
|
|
143
|
+
|
|
144
|
+
def argument(
|
|
145
|
+
name: str,
|
|
146
|
+
type: Any = str,
|
|
147
|
+
required: bool = True,
|
|
148
|
+
help: str = None,
|
|
149
|
+
default: Any = None
|
|
150
|
+
) -> Callable:
|
|
151
|
+
"""
|
|
152
|
+
Decorator to add a command argument.
|
|
153
|
+
|
|
154
|
+
Arguments are positional parameters that must be provided in order.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
name: Argument name
|
|
158
|
+
type: Expected type
|
|
159
|
+
required: Whether argument is required
|
|
160
|
+
help: Help text
|
|
161
|
+
default: Default value if not required
|
|
162
|
+
|
|
163
|
+
Example:
|
|
164
|
+
@command()
|
|
165
|
+
@argument("name")
|
|
166
|
+
@argument("count", type=int, default=1)
|
|
167
|
+
def greet(name: str, count: int):
|
|
168
|
+
'''Greet someone multiple times'''
|
|
169
|
+
for _ in range(count):
|
|
170
|
+
print(f"Hello {name}!")
|
|
171
|
+
"""
|
|
172
|
+
def decorator(f: Callable) -> Callable:
|
|
173
|
+
if not hasattr(f, '_arguments'):
|
|
174
|
+
f._arguments = []
|
|
175
|
+
|
|
176
|
+
f._arguments.append({
|
|
177
|
+
'name': name,
|
|
178
|
+
'type': type,
|
|
179
|
+
'required': required,
|
|
180
|
+
'help': help,
|
|
181
|
+
'default': default
|
|
182
|
+
})
|
|
183
|
+
return f
|
|
184
|
+
return decorator
|
|
185
|
+
|
|
186
|
+
def flag(
|
|
187
|
+
name: str,
|
|
188
|
+
help: str = None,
|
|
189
|
+
hidden: bool = False
|
|
190
|
+
) -> Callable:
|
|
191
|
+
"""
|
|
192
|
+
Decorator to add a boolean flag option.
|
|
193
|
+
|
|
194
|
+
Flags are special options that don't take a value - they're either
|
|
195
|
+
present (True) or absent (False).
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
name: Flag name
|
|
199
|
+
help: Help text
|
|
200
|
+
hidden: Whether to hide from help output
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
@command()
|
|
204
|
+
@flag("--verbose", help="Enable verbose output")
|
|
205
|
+
def process(verbose: bool):
|
|
206
|
+
'''Process data'''
|
|
207
|
+
if verbose:
|
|
208
|
+
print("Verbose mode enabled")
|
|
209
|
+
"""
|
|
210
|
+
def decorator(f: Callable) -> Callable:
|
|
211
|
+
if not hasattr(f, '_options'):
|
|
212
|
+
f._options = []
|
|
213
|
+
|
|
214
|
+
f._options.append({
|
|
215
|
+
'param_decls': [name],
|
|
216
|
+
'is_flag': True,
|
|
217
|
+
'help': help,
|
|
218
|
+
'hidden': hidden
|
|
219
|
+
})
|
|
220
|
+
return f
|
|
221
|
+
return decorator
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""Option decorators for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
4
|
+
|
|
5
|
+
def option(
|
|
6
|
+
*param_decls: str,
|
|
7
|
+
type: Any = str,
|
|
8
|
+
required: bool = False,
|
|
9
|
+
default: Any = None,
|
|
10
|
+
help: str = None,
|
|
11
|
+
is_flag: bool = False,
|
|
12
|
+
multiple: bool = False,
|
|
13
|
+
count: bool = False,
|
|
14
|
+
prompt: bool = False,
|
|
15
|
+
hide_input: bool = False,
|
|
16
|
+
confirmation_prompt: bool = False,
|
|
17
|
+
prompt_required: bool = True,
|
|
18
|
+
show_default: bool = True,
|
|
19
|
+
choices: Optional[List[Any]] = None,
|
|
20
|
+
case_sensitive: bool = True,
|
|
21
|
+
callback: Optional[Callable] = None,
|
|
22
|
+
hidden: bool = False
|
|
23
|
+
) -> Callable:
|
|
24
|
+
"""
|
|
25
|
+
Decorator to add an option to a command.
|
|
26
|
+
|
|
27
|
+
Options are named parameters that can be provided in any order.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
param_decls: Option names (e.g., "--name", "-n")
|
|
31
|
+
type: Expected type
|
|
32
|
+
required: Whether option is required
|
|
33
|
+
default: Default value
|
|
34
|
+
help: Help text
|
|
35
|
+
is_flag: Whether option is a boolean flag
|
|
36
|
+
multiple: Whether option can be specified multiple times
|
|
37
|
+
count: Whether option counts occurrences
|
|
38
|
+
prompt: Whether to prompt for value if not provided
|
|
39
|
+
hide_input: Whether to hide input when prompting
|
|
40
|
+
confirmation_prompt: Whether to prompt for confirmation
|
|
41
|
+
prompt_required: Whether prompt is required
|
|
42
|
+
show_default: Whether to show default in help
|
|
43
|
+
choices: List of valid choices
|
|
44
|
+
case_sensitive: Whether choices are case sensitive
|
|
45
|
+
callback: Function to process/validate value
|
|
46
|
+
hidden: Whether to hide from help output
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
@command()
|
|
50
|
+
@option("--count", "-c", type=int, default=1)
|
|
51
|
+
@option("--format", type=str, choices=["json", "yaml"])
|
|
52
|
+
@option("--verbose", is_flag=True)
|
|
53
|
+
def process(count: int, format: str, verbose: bool):
|
|
54
|
+
'''Process data'''
|
|
55
|
+
if verbose:
|
|
56
|
+
print(f"Processing {count} items as {format}")
|
|
57
|
+
"""
|
|
58
|
+
def decorator(f: Callable) -> Callable:
|
|
59
|
+
if not hasattr(f, '_options'):
|
|
60
|
+
f._options = []
|
|
61
|
+
|
|
62
|
+
f._options.append({
|
|
63
|
+
'param_decls': param_decls,
|
|
64
|
+
'type': type,
|
|
65
|
+
'required': required,
|
|
66
|
+
'default': default,
|
|
67
|
+
'help': help,
|
|
68
|
+
'is_flag': is_flag,
|
|
69
|
+
'multiple': multiple,
|
|
70
|
+
'count': count,
|
|
71
|
+
'prompt': prompt,
|
|
72
|
+
'hide_input': hide_input,
|
|
73
|
+
'confirmation_prompt': confirmation_prompt,
|
|
74
|
+
'prompt_required': prompt_required,
|
|
75
|
+
'show_default': show_default,
|
|
76
|
+
'choices': choices,
|
|
77
|
+
'case_sensitive': case_sensitive,
|
|
78
|
+
'callback': callback,
|
|
79
|
+
'hidden': hidden
|
|
80
|
+
})
|
|
81
|
+
return f
|
|
82
|
+
return decorator
|
|
83
|
+
|
|
84
|
+
def envvar(
|
|
85
|
+
name: str,
|
|
86
|
+
type: Any = str,
|
|
87
|
+
required: bool = False,
|
|
88
|
+
default: Any = None,
|
|
89
|
+
help: str = None
|
|
90
|
+
) -> Callable:
|
|
91
|
+
"""
|
|
92
|
+
Decorator to load option value from environment variable.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
name: Environment variable name
|
|
96
|
+
type: Expected type
|
|
97
|
+
required: Whether variable is required
|
|
98
|
+
default: Default value if not set
|
|
99
|
+
help: Help text
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
@command()
|
|
103
|
+
@envvar("API_KEY", required=True)
|
|
104
|
+
@envvar("API_URL", default="https://api.example.com")
|
|
105
|
+
def api_call(api_key: str, api_url: str):
|
|
106
|
+
'''Make API call'''
|
|
107
|
+
print(f"Calling {api_url} with key {api_key}")
|
|
108
|
+
"""
|
|
109
|
+
def decorator(f: Callable) -> Callable:
|
|
110
|
+
if not hasattr(f, '_envvars'):
|
|
111
|
+
f._envvars = []
|
|
112
|
+
|
|
113
|
+
f._envvars.append({
|
|
114
|
+
'name': name,
|
|
115
|
+
'type': type,
|
|
116
|
+
'required': required,
|
|
117
|
+
'default': default,
|
|
118
|
+
'help': help
|
|
119
|
+
})
|
|
120
|
+
return f
|
|
121
|
+
return decorator
|
|
122
|
+
|
|
123
|
+
def config_file(
|
|
124
|
+
path: str = None,
|
|
125
|
+
section: str = None,
|
|
126
|
+
required: bool = False,
|
|
127
|
+
auto_create: bool = True,
|
|
128
|
+
format: str = 'json'
|
|
129
|
+
) -> Callable:
|
|
130
|
+
"""
|
|
131
|
+
Decorator to load configuration from file.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
path: Config file path
|
|
135
|
+
section: Config section to load
|
|
136
|
+
required: Whether config is required
|
|
137
|
+
auto_create: Whether to create file if missing
|
|
138
|
+
format: File format (json, yaml, ini)
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
@command()
|
|
142
|
+
@config_file("~/.myapp/config.json")
|
|
143
|
+
def setup(config: dict):
|
|
144
|
+
'''Setup application'''
|
|
145
|
+
print(f"Database: {config.get('database')}")
|
|
146
|
+
|
|
147
|
+
@command()
|
|
148
|
+
@config_file("config.ini", section="api")
|
|
149
|
+
def api(config: dict):
|
|
150
|
+
'''Make API call'''
|
|
151
|
+
print(f"URL: {config.get('url')}")
|
|
152
|
+
"""
|
|
153
|
+
def decorator(f: Callable) -> Callable:
|
|
154
|
+
f._config = {
|
|
155
|
+
'path': path,
|
|
156
|
+
'section': section,
|
|
157
|
+
'required': required,
|
|
158
|
+
'auto_create': auto_create,
|
|
159
|
+
'format': format
|
|
160
|
+
}
|
|
161
|
+
return f
|
|
162
|
+
return decorator
|
|
163
|
+
|
|
164
|
+
def version_option(
|
|
165
|
+
version: str = None,
|
|
166
|
+
prog_name: str = None,
|
|
167
|
+
message: str = None,
|
|
168
|
+
package_name: str = None
|
|
169
|
+
) -> Callable:
|
|
170
|
+
"""
|
|
171
|
+
Decorator to add version option to command.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
version: Version string
|
|
175
|
+
prog_name: Program name
|
|
176
|
+
message: Custom version message
|
|
177
|
+
package_name: Package name to get version from
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
@command()
|
|
181
|
+
@version_option(version="1.0.0")
|
|
182
|
+
def main():
|
|
183
|
+
'''Main command'''
|
|
184
|
+
pass
|
|
185
|
+
"""
|
|
186
|
+
def decorator(f: Callable) -> Callable:
|
|
187
|
+
f._version = {
|
|
188
|
+
'version': version,
|
|
189
|
+
'prog_name': prog_name,
|
|
190
|
+
'message': message,
|
|
191
|
+
'package_name': package_name
|
|
192
|
+
}
|
|
193
|
+
return f
|
|
194
|
+
return decorator
|
|
195
|
+
|
|
196
|
+
def help_option(
|
|
197
|
+
param_decls: List[str] = None,
|
|
198
|
+
help: str = None
|
|
199
|
+
) -> Callable:
|
|
200
|
+
"""
|
|
201
|
+
Decorator to customize help option.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
param_decls: Help option flags
|
|
205
|
+
help: Help text
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
@command()
|
|
209
|
+
@help_option(["--help", "-h"], "Show this message")
|
|
210
|
+
def main():
|
|
211
|
+
'''Main command'''
|
|
212
|
+
pass
|
|
213
|
+
"""
|
|
214
|
+
def decorator(f: Callable) -> Callable:
|
|
215
|
+
f._help_option = {
|
|
216
|
+
'param_decls': param_decls or ['--help', '-h'],
|
|
217
|
+
'help': help or 'Show this message and exit.'
|
|
218
|
+
}
|
|
219
|
+
return f
|
|
220
|
+
return decorator
|
|
@@ -0,0 +1,252 @@
|
|
|
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.progress import (
|
|
9
|
+
Progress,
|
|
10
|
+
SpinnerColumn,
|
|
11
|
+
TextColumn,
|
|
12
|
+
BarColumn,
|
|
13
|
+
TaskProgressColumn,
|
|
14
|
+
TimeRemainingColumn
|
|
15
|
+
)
|
|
16
|
+
from rich.panel import Panel
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
def table_output(
|
|
21
|
+
headers: List[str],
|
|
22
|
+
title: Optional[str] = None,
|
|
23
|
+
style: str = "default",
|
|
24
|
+
show_lines: bool = False,
|
|
25
|
+
expand: bool = False
|
|
26
|
+
) -> Callable:
|
|
27
|
+
"""
|
|
28
|
+
Decorator to format command output as a table.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
headers: Column headers
|
|
32
|
+
title: Table title
|
|
33
|
+
style: Table style
|
|
34
|
+
show_lines: Show row/column lines
|
|
35
|
+
expand: Expand table to terminal width
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
@command()
|
|
39
|
+
@table_output(["ID", "Name", "Status"])
|
|
40
|
+
def list_users():
|
|
41
|
+
'''List all users'''
|
|
42
|
+
return [
|
|
43
|
+
[1, "John", "Active"],
|
|
44
|
+
[2, "Jane", "Inactive"]
|
|
45
|
+
]
|
|
46
|
+
"""
|
|
47
|
+
def decorator(f: Callable) -> Callable:
|
|
48
|
+
@wraps(f)
|
|
49
|
+
def wrapper(*args, **kwargs):
|
|
50
|
+
result = f(*args, **kwargs)
|
|
51
|
+
if result:
|
|
52
|
+
table = Table(
|
|
53
|
+
title=title,
|
|
54
|
+
show_header=True,
|
|
55
|
+
header_style="bold blue",
|
|
56
|
+
show_lines=show_lines,
|
|
57
|
+
expand=expand
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Add columns
|
|
61
|
+
for header in headers:
|
|
62
|
+
table.add_column(header)
|
|
63
|
+
|
|
64
|
+
# Add rows
|
|
65
|
+
for row in result:
|
|
66
|
+
table.add_row(*[str(cell) for cell in row])
|
|
67
|
+
|
|
68
|
+
console.print(table)
|
|
69
|
+
return result
|
|
70
|
+
return wrapper
|
|
71
|
+
return decorator
|
|
72
|
+
|
|
73
|
+
def progress(
|
|
74
|
+
description: str = None,
|
|
75
|
+
total: Optional[int] = None,
|
|
76
|
+
transient: bool = False,
|
|
77
|
+
show_bar: bool = True,
|
|
78
|
+
show_percentage: bool = True,
|
|
79
|
+
show_time: bool = True
|
|
80
|
+
) -> Callable:
|
|
81
|
+
"""
|
|
82
|
+
Decorator to show progress for long-running commands.
|
|
83
|
+
|
|
84
|
+
The decorated function should be a generator that yields
|
|
85
|
+
progress updates.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
description: Task description
|
|
89
|
+
total: Total number of steps
|
|
90
|
+
transient: Remove progress when done
|
|
91
|
+
show_bar: Show progress bar
|
|
92
|
+
show_percentage: Show percentage complete
|
|
93
|
+
show_time: Show time remaining
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
@command()
|
|
97
|
+
@progress("Processing files")
|
|
98
|
+
def process(files: List[str]):
|
|
99
|
+
'''Process multiple files'''
|
|
100
|
+
for file in files:
|
|
101
|
+
# Process file
|
|
102
|
+
yield f"Processing {file}..."
|
|
103
|
+
"""
|
|
104
|
+
def decorator(f: Callable) -> Callable:
|
|
105
|
+
@wraps(f)
|
|
106
|
+
def wrapper(*args, **kwargs):
|
|
107
|
+
columns = []
|
|
108
|
+
columns.append(SpinnerColumn())
|
|
109
|
+
columns.append(TextColumn("[progress.description]{task.description}"))
|
|
110
|
+
|
|
111
|
+
if show_bar:
|
|
112
|
+
columns.append(BarColumn())
|
|
113
|
+
if show_percentage:
|
|
114
|
+
columns.append(TaskProgressColumn())
|
|
115
|
+
if show_time:
|
|
116
|
+
columns.append(TimeRemainingColumn())
|
|
117
|
+
|
|
118
|
+
with Progress(*columns, transient=transient) as progress:
|
|
119
|
+
task = progress.add_task(
|
|
120
|
+
description or f.__name__,
|
|
121
|
+
total=total
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
for update in f(*args, **kwargs):
|
|
126
|
+
if isinstance(update, (int, float)):
|
|
127
|
+
# Update progress by number
|
|
128
|
+
progress.update(task, advance=update)
|
|
129
|
+
elif isinstance(update, str):
|
|
130
|
+
# Update description
|
|
131
|
+
progress.update(task, description=update)
|
|
132
|
+
elif isinstance(update, dict):
|
|
133
|
+
# Update multiple attributes
|
|
134
|
+
progress.update(task, **update)
|
|
135
|
+
else:
|
|
136
|
+
# Just advance by 1
|
|
137
|
+
progress.update(task, advance=1)
|
|
138
|
+
except Exception as e:
|
|
139
|
+
progress.update(task, description=f"Error: {str(e)}")
|
|
140
|
+
raise
|
|
141
|
+
finally:
|
|
142
|
+
progress.update(task, completed=total or 100)
|
|
143
|
+
|
|
144
|
+
return wrapper
|
|
145
|
+
return decorator
|
|
146
|
+
|
|
147
|
+
def panel_output(
|
|
148
|
+
title: Optional[str] = None,
|
|
149
|
+
style: str = "default",
|
|
150
|
+
expand: bool = True,
|
|
151
|
+
padding: Union[int, tuple] = 1
|
|
152
|
+
) -> Callable:
|
|
153
|
+
"""
|
|
154
|
+
Decorator to display command output in a panel.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
title: Panel title
|
|
158
|
+
style: Panel style
|
|
159
|
+
expand: Expand panel to terminal width
|
|
160
|
+
padding: Panel padding
|
|
161
|
+
|
|
162
|
+
Example:
|
|
163
|
+
@command()
|
|
164
|
+
@panel_output(title="System Status")
|
|
165
|
+
def status():
|
|
166
|
+
'''Show system status'''
|
|
167
|
+
return "All systems operational"
|
|
168
|
+
"""
|
|
169
|
+
def decorator(f: Callable) -> Callable:
|
|
170
|
+
@wraps(f)
|
|
171
|
+
def wrapper(*args, **kwargs):
|
|
172
|
+
result = f(*args, **kwargs)
|
|
173
|
+
if result:
|
|
174
|
+
panel = Panel(
|
|
175
|
+
str(result),
|
|
176
|
+
title=title,
|
|
177
|
+
style=style,
|
|
178
|
+
expand=expand,
|
|
179
|
+
padding=padding
|
|
180
|
+
)
|
|
181
|
+
console.print(panel)
|
|
182
|
+
return result
|
|
183
|
+
return wrapper
|
|
184
|
+
return decorator
|
|
185
|
+
|
|
186
|
+
def format_output(
|
|
187
|
+
template: str,
|
|
188
|
+
style: Optional[str] = None
|
|
189
|
+
) -> Callable:
|
|
190
|
+
"""
|
|
191
|
+
Decorator to format command output using a template.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
template: Format string template
|
|
195
|
+
style: Rich style string
|
|
196
|
+
|
|
197
|
+
Example:
|
|
198
|
+
@command()
|
|
199
|
+
@format_output("Created user {name} with ID {id}")
|
|
200
|
+
def create_user(name: str):
|
|
201
|
+
'''Create a new user'''
|
|
202
|
+
return {"name": name, "id": 123}
|
|
203
|
+
"""
|
|
204
|
+
def decorator(f: Callable) -> Callable:
|
|
205
|
+
@wraps(f)
|
|
206
|
+
def wrapper(*args, **kwargs):
|
|
207
|
+
result = f(*args, **kwargs)
|
|
208
|
+
if result:
|
|
209
|
+
if isinstance(result, dict):
|
|
210
|
+
output = template.format(**result)
|
|
211
|
+
else:
|
|
212
|
+
output = template.format(result)
|
|
213
|
+
|
|
214
|
+
if style:
|
|
215
|
+
console.print(output, style=style)
|
|
216
|
+
else:
|
|
217
|
+
console.print(output)
|
|
218
|
+
return result
|
|
219
|
+
return wrapper
|
|
220
|
+
return decorator
|
|
221
|
+
|
|
222
|
+
def pager_output(
|
|
223
|
+
use_pager: bool = True,
|
|
224
|
+
style: Optional[str] = None
|
|
225
|
+
) -> Callable:
|
|
226
|
+
"""
|
|
227
|
+
Decorator to display command output in a pager.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
use_pager: Whether to use pager
|
|
231
|
+
style: Rich style string
|
|
232
|
+
|
|
233
|
+
Example:
|
|
234
|
+
@command()
|
|
235
|
+
@pager_output()
|
|
236
|
+
def show_logs():
|
|
237
|
+
'''Show application logs'''
|
|
238
|
+
return "Very long log output..."
|
|
239
|
+
"""
|
|
240
|
+
def decorator(f: Callable) -> Callable:
|
|
241
|
+
@wraps(f)
|
|
242
|
+
def wrapper(*args, **kwargs):
|
|
243
|
+
result = f(*args, **kwargs)
|
|
244
|
+
if result:
|
|
245
|
+
with console.pager(styles=use_pager):
|
|
246
|
+
if style:
|
|
247
|
+
console.print(result, style=style)
|
|
248
|
+
else:
|
|
249
|
+
console.print(result)
|
|
250
|
+
return result
|
|
251
|
+
return wrapper
|
|
252
|
+
return decorator
|
|
@@ -0,0 +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
|