webscout 6.2b0__py3-none-any.whl → 6.4__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.

Files changed (97) hide show
  1. webscout/AIauto.py +191 -176
  2. webscout/AIbase.py +112 -239
  3. webscout/AIutel.py +488 -1130
  4. webscout/Agents/functioncall.py +248 -198
  5. webscout/Bing_search.py +250 -153
  6. webscout/DWEBS.py +454 -178
  7. webscout/Extra/__init__.py +2 -1
  8. webscout/Extra/autocoder/__init__.py +9 -0
  9. webscout/Extra/autocoder/autocoder_utiles.py +121 -0
  10. webscout/Extra/autocoder/rawdog.py +681 -0
  11. webscout/Extra/autollama.py +246 -195
  12. webscout/Extra/gguf.py +441 -226
  13. webscout/Extra/weather.py +172 -67
  14. webscout/LLM.py +442 -100
  15. webscout/Litlogger/__init__.py +681 -0
  16. webscout/Local/formats.py +4 -2
  17. webscout/Provider/Amigo.py +19 -10
  18. webscout/Provider/Andi.py +0 -33
  19. webscout/Provider/Blackboxai.py +4 -204
  20. webscout/Provider/DARKAI.py +1 -1
  21. webscout/Provider/EDITEE.py +1 -1
  22. webscout/Provider/Llama3.py +1 -1
  23. webscout/Provider/Marcus.py +137 -0
  24. webscout/Provider/NinjaChat.py +1 -1
  25. webscout/Provider/PI.py +221 -207
  26. webscout/Provider/Perplexity.py +598 -598
  27. webscout/Provider/RoboCoders.py +206 -0
  28. webscout/Provider/TTI/AiForce/__init__.py +22 -0
  29. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -0
  30. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -0
  31. webscout/Provider/TTI/Nexra/__init__.py +22 -0
  32. webscout/Provider/TTI/Nexra/async_nexra.py +286 -0
  33. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -0
  34. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -0
  35. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -0
  36. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -0
  37. webscout/Provider/TTI/__init__.py +3 -4
  38. webscout/Provider/TTI/artbit/__init__.py +22 -0
  39. webscout/Provider/TTI/artbit/async_artbit.py +184 -0
  40. webscout/Provider/TTI/artbit/sync_artbit.py +176 -0
  41. webscout/Provider/TTI/blackbox/__init__.py +4 -0
  42. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -0
  43. webscout/Provider/TTI/{blackboximage.py → blackbox/sync_blackbox.py} +199 -153
  44. webscout/Provider/TTI/deepinfra/__init__.py +4 -0
  45. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -0
  46. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -0
  47. webscout/Provider/TTI/huggingface/__init__.py +22 -0
  48. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -0
  49. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -0
  50. webscout/Provider/TTI/imgninza/__init__.py +4 -0
  51. webscout/Provider/TTI/imgninza/async_ninza.py +214 -0
  52. webscout/Provider/TTI/{imgninza.py → imgninza/sync_ninza.py} +209 -136
  53. webscout/Provider/TTI/talkai/__init__.py +4 -0
  54. webscout/Provider/TTI/talkai/async_talkai.py +229 -0
  55. webscout/Provider/TTI/talkai/sync_talkai.py +207 -0
  56. webscout/Provider/__init__.py +146 -132
  57. webscout/Provider/askmyai.py +158 -0
  58. webscout/Provider/cerebras.py +227 -206
  59. webscout/Provider/geminiapi.py +208 -198
  60. webscout/Provider/llama3mitril.py +180 -0
  61. webscout/Provider/llmchat.py +203 -0
  62. webscout/Provider/mhystical.py +176 -0
  63. webscout/Provider/perplexitylabs.py +265 -0
  64. webscout/Provider/talkai.py +196 -0
  65. webscout/Provider/twitterclone.py +251 -244
  66. webscout/Provider/typegpt.py +359 -0
  67. webscout/__init__.py +28 -23
  68. webscout/__main__.py +5 -5
  69. webscout/cli.py +327 -347
  70. webscout/conversation.py +227 -0
  71. webscout/exceptions.py +161 -29
  72. webscout/litagent/__init__.py +172 -0
  73. webscout/litprinter/__init__.py +831 -0
  74. webscout/optimizers.py +270 -0
  75. webscout/prompt_manager.py +279 -0
  76. webscout/swiftcli/__init__.py +810 -0
  77. webscout/transcriber.py +479 -551
  78. webscout/update_checker.py +125 -0
  79. webscout/version.py +1 -1
  80. webscout-6.4.dist-info/LICENSE.md +211 -0
  81. {webscout-6.2b0.dist-info → webscout-6.4.dist-info}/METADATA +34 -55
  82. webscout-6.4.dist-info/RECORD +154 -0
  83. webscout/Provider/TTI/AIuncensored.py +0 -103
  84. webscout/Provider/TTI/Nexra.py +0 -120
  85. webscout/Provider/TTI/PollinationsAI.py +0 -138
  86. webscout/Provider/TTI/WebSimAI.py +0 -142
  87. webscout/Provider/TTI/aiforce.py +0 -160
  88. webscout/Provider/TTI/artbit.py +0 -141
  89. webscout/Provider/TTI/deepinfra.py +0 -148
  90. webscout/Provider/TTI/huggingface.py +0 -155
  91. webscout/models.py +0 -23
  92. webscout-6.2b0.dist-info/LICENSE.md +0 -50
  93. webscout-6.2b0.dist-info/RECORD +0 -118
  94. /webscout/{g4f.py → gpt4free.py} +0 -0
  95. {webscout-6.2b0.dist-info → webscout-6.4.dist-info}/WHEEL +0 -0
  96. {webscout-6.2b0.dist-info → webscout-6.4.dist-info}/entry_points.txt +0 -0
  97. {webscout-6.2b0.dist-info → webscout-6.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,810 @@
1
+ """
2
+ SwiftCLI - A powerful Python CLI framework
3
+
4
+ A modern, feature-rich CLI framework for building awesome command-line applications.
5
+ Built with love for the Python community!
6
+
7
+ Basic Usage:
8
+ >>> from swiftcli import CLI
9
+ >>> app = CLI(name="my-app", help="My awesome CLI app")
10
+ >>> @app.command()
11
+ ... def hello(name: str):
12
+ ... '''Say hello to someone'''
13
+ ... print(f"Hello {name}!")
14
+ >>> app.run()
15
+
16
+ Advanced Usage:
17
+ >>> @app.group()
18
+ ... def db():
19
+ ... '''Database commands'''
20
+ ... pass
21
+ >>>
22
+ >>> @db.command()
23
+ ... def migrate():
24
+ ... '''Run database migrations'''
25
+ ... print("Running migrations...")
26
+
27
+ For more examples, check out the documentation!
28
+ """
29
+
30
+ import os
31
+ import sys
32
+ import json
33
+ import inspect
34
+ from typing import Any, Dict, List, Optional, Type, Union, Callable
35
+ from functools import wraps
36
+ from pathlib import Path
37
+ from rich.console import Console
38
+ from rich.table import Table
39
+ from rich.progress import Progress, SpinnerColumn, TextColumn
40
+ from rich.theme import Theme
41
+
42
+ # Console setup
43
+ console = Console()
44
+
45
+ class UsageError(Exception):
46
+ """Raised when CLI is used incorrectly"""
47
+ pass
48
+
49
+ class BadParameter(UsageError):
50
+ """Raised when a parameter is invalid"""
51
+ pass
52
+
53
+ class Context:
54
+ """
55
+ Context object that holds state for the CLI app.
56
+
57
+ Attributes:
58
+ cli (CLI): The CLI application instance.
59
+ parent (Context): The parent context.
60
+ command (str): The current command.
61
+ obj (Any): The current object.
62
+ params (Dict[str, Any]): The current parameters.
63
+ """
64
+ def __init__(
65
+ self,
66
+ cli: 'CLI',
67
+ parent: Optional['Context'] = None,
68
+ command: Optional[str] = None,
69
+ obj: Any = None
70
+ ):
71
+ self.cli = cli
72
+ self.parent = parent
73
+ self.command = command
74
+ self.obj = obj
75
+ self.params = {}
76
+
77
+ class Plugin:
78
+ """
79
+ Base class for SwiftCLI plugins.
80
+
81
+ Attributes:
82
+ app (CLI): The CLI application instance.
83
+ enabled (bool): Whether the plugin is enabled.
84
+ config (Dict[str, Any]): The plugin configuration.
85
+ """
86
+ def __init__(self):
87
+ self.app = None
88
+ self.enabled = True
89
+ self.config: Dict[str, Any] = {}
90
+
91
+ def init_app(self, app):
92
+ """Initialize plugin with CLI app instance"""
93
+ self.app = app
94
+
95
+ def before_command(self, command: str, args: List[str]) -> Optional[bool]:
96
+ """Called before command execution"""
97
+ pass
98
+
99
+ def after_command(self, command: str, args: List[str], result: Any):
100
+ """Called after command execution"""
101
+ pass
102
+
103
+ def on_error(self, command: str, error: Exception):
104
+ """Called when command raises an error"""
105
+ pass
106
+
107
+ def on_help(self, command: str) -> Optional[str]:
108
+ """Called when help is requested for a command"""
109
+ pass
110
+
111
+ def on_completion(self, command: str, incomplete: str) -> List[str]:
112
+ """Called when shell completion is requested"""
113
+ return []
114
+
115
+ class PluginManager:
116
+ """
117
+ Manages SwiftCLI plugins.
118
+
119
+ Attributes:
120
+ plugins (List[Plugin]): The list of plugins.
121
+ plugin_dir (str): The plugin directory.
122
+ """
123
+ def __init__(self):
124
+ self.plugins: List[Plugin] = []
125
+ self.plugin_dir = os.path.expanduser("~/.swiftcli/plugins")
126
+ os.makedirs(self.plugin_dir, exist_ok=True)
127
+ sys.path.append(self.plugin_dir)
128
+
129
+ def register(self, plugin: Plugin):
130
+ """Register a new plugin"""
131
+ self.plugins.append(plugin)
132
+
133
+ def load_plugins(self):
134
+ """Load all plugins from plugin directory"""
135
+ for file in Path(self.plugin_dir).glob("*.py"):
136
+ if file.name.startswith("_"):
137
+ continue
138
+ try:
139
+ module = importlib.import_module(file.stem)
140
+ for attr_name in dir(module):
141
+ attr = getattr(module, attr_name)
142
+ if (isinstance(attr, type) and
143
+ issubclass(attr, Plugin) and
144
+ attr is not Plugin):
145
+ plugin = attr()
146
+ self.register(plugin)
147
+ except Exception as e:
148
+ console.print(f"[red]Error loading plugin {file.name}: {e}[/red]")
149
+
150
+ def before_command(self, command: str, args: List[str]) -> bool:
151
+ """Run before_command hooks"""
152
+ for plugin in self.plugins:
153
+ if not plugin.enabled:
154
+ continue
155
+ try:
156
+ result = plugin.before_command(command, args)
157
+ if result is False:
158
+ return False
159
+ except Exception as e:
160
+ console.print(f"[red]Error in plugin {plugin.__class__.__name__}: {e}[/red]")
161
+ return True
162
+
163
+ def after_command(self, command: str, args: List[str], result: Any):
164
+ """Run after_command hooks"""
165
+ for plugin in self.plugins:
166
+ if not plugin.enabled:
167
+ continue
168
+ try:
169
+ plugin.after_command(command, args, result)
170
+ except Exception as e:
171
+ console.print(f"[red]Error in plugin {plugin.__class__.__name__}: {e}[/red]")
172
+
173
+ def on_error(self, command: str, error: Exception):
174
+ """Run error hooks"""
175
+ for plugin in self.plugins:
176
+ if not plugin.enabled:
177
+ continue
178
+ try:
179
+ plugin.on_error(command, error)
180
+ except Exception as e:
181
+ console.print(f"[red]Error in plugin {plugin.__class__.__name__}: {e}[/red]")
182
+
183
+ class Group:
184
+ """
185
+ Command group that can contain subcommands and be chained.
186
+
187
+ Basic Usage:
188
+ >>> @app.group()
189
+ ... def db():
190
+ ... '''Database commands'''
191
+ ... pass
192
+ >>> @db.command()
193
+ ... def migrate():
194
+ ... '''Run migrations'''
195
+ ... pass
196
+
197
+ Advanced Usage:
198
+ >>> @app.group(chain=True)
199
+ ... def process():
200
+ ... '''Process data'''
201
+ ... pass
202
+ >>> @process.command()
203
+ ... def validate():
204
+ ... '''Validate data'''
205
+ ... pass
206
+ """
207
+ def __init__(
208
+ self,
209
+ name: str = None,
210
+ help: str = None,
211
+ chain: bool = False,
212
+ invoke_without_command: bool = False
213
+ ):
214
+ self.name = name
215
+ self.help = help
216
+ self.chain = chain
217
+ self.invoke_without_command = invoke_without_command
218
+ self.commands = {}
219
+
220
+ def command(
221
+ self,
222
+ name: str = None,
223
+ help: str = None,
224
+ aliases: List[str] = None,
225
+ hidden: bool = False
226
+ ):
227
+ """Register a new command"""
228
+ def decorator(f):
229
+ cmd_name = name or f.__name__
230
+ self.commands[cmd_name] = {
231
+ 'func': f,
232
+ 'help': help or f.__doc__,
233
+ 'aliases': aliases or [],
234
+ 'hidden': hidden
235
+ }
236
+ return f
237
+ return decorator
238
+
239
+ def group(self, *args, **kwargs):
240
+ """Create a subgroup"""
241
+ def decorator(f):
242
+ subgroup = Group(*args, **kwargs)
243
+ self.commands[subgroup.name] = subgroup
244
+ return subgroup
245
+ return decorator
246
+
247
+ def run(self, args: List[str]):
248
+ """Run the group command"""
249
+ if not args or args[0] in ['-h', '--help']:
250
+ self._print_help()
251
+ return
252
+
253
+ command_name = args[0]
254
+ command_args = args[1:]
255
+
256
+ if command_name not in self.commands:
257
+ console.print(f"[red]Unknown command: {command_name}[/red]")
258
+ self._print_help()
259
+ return 1
260
+
261
+ command = self.commands[command_name]
262
+ try:
263
+ result = command['func'](**self._parse_args(command, command_args))
264
+ if self.chain and result is not None:
265
+ return result
266
+ except Exception as e:
267
+ console.print(f"[red]Error: {str(e)}[/red]")
268
+ return 1
269
+
270
+ def _parse_args(self, command: Dict, args: List[str]) -> Dict[str, Any]:
271
+ """Parse command arguments"""
272
+ params = {}
273
+ func = command['func']
274
+ sig = inspect.signature(func)
275
+
276
+ # Handle options
277
+ if hasattr(func, '_options'):
278
+ for opt in func._options:
279
+ # Get the destination parameter name from the longest option
280
+ param_decls = sorted(opt['param_decls'], key=len, reverse=True)
281
+ param_name = param_decls[0].lstrip('-').replace('-', '_')
282
+
283
+ # If there's a parameter name in the signature, use that instead
284
+ for param in sig.parameters.values():
285
+ if param.name in [d.lstrip('-').replace('-', '_') for d in param_decls]:
286
+ param_name = param.name
287
+ break
288
+
289
+ found = False
290
+ multiple_values = []
291
+
292
+ # Check for long and short options
293
+ i = 0
294
+ while i < len(args):
295
+ if args[i] in opt['param_decls']:
296
+ if opt.get('is_flag', False):
297
+ if opt.get('multiple', False):
298
+ multiple_values.append(True)
299
+ else:
300
+ params[param_name] = True
301
+ else:
302
+ if i + 1 < len(args):
303
+ value = args[i + 1]
304
+ # Convert value to the correct type
305
+ if 'type' in opt:
306
+ try:
307
+ value = opt['type'](value)
308
+ except ValueError:
309
+ raise UsageError(f"Invalid value for {args[i]}: {value}")
310
+
311
+ if opt.get('multiple', False):
312
+ multiple_values.append(value)
313
+ else:
314
+ params[param_name] = value
315
+ args.pop(i + 1)
316
+ else:
317
+ raise UsageError(f"Option {args[i]} requires a value")
318
+ args.pop(i)
319
+ found = True
320
+ if not opt.get('multiple', False):
321
+ break
322
+ else:
323
+ i += 1
324
+
325
+ # Set multiple values if any
326
+ if multiple_values:
327
+ params[param_name] = multiple_values
328
+
329
+ # Handle required options
330
+ if not found and opt.get('required', False):
331
+ raise UsageError(f"Option {opt['param_decls'][0]} is required")
332
+
333
+ # Set default value if not found
334
+ if not found and 'default' in opt:
335
+ params[param_name] = opt['default']
336
+
337
+ # Handle arguments
338
+ if hasattr(func, '_arguments'):
339
+ for i, arg in enumerate(func._arguments):
340
+ if i < len(args):
341
+ value = args[i]
342
+ # Convert value to the correct type
343
+ if 'type' in arg:
344
+ try:
345
+ value = arg['type'](value)
346
+ except ValueError:
347
+ raise UsageError(f"Invalid value for {arg['name']}: {value}")
348
+ params[arg['name']] = value
349
+ elif arg.get('required', True):
350
+ raise UsageError(f"Argument {arg['name']} is required")
351
+ elif 'default' in arg:
352
+ params[arg['name']] = arg['default']
353
+
354
+ # Handle environment variables
355
+ if hasattr(func, '_envvars'):
356
+ for env in func._envvars:
357
+ value = os.environ.get(env['name'])
358
+ if env.get('required', False) and not value:
359
+ raise UsageError(f"Environment variable {env['name']} is required")
360
+ if value:
361
+ # Convert value to the correct type
362
+ if 'type' in env:
363
+ try:
364
+ value = env['type'](value)
365
+ except ValueError:
366
+ raise UsageError(f"Invalid value for {env['name']}: {value}")
367
+ params[env['name'].lower()] = value
368
+
369
+ return params
370
+
371
+ def _print_help(self):
372
+ """Print group help message"""
373
+ console.print(f"\n{self.name} commands:")
374
+ if self.help:
375
+ console.print(f"\n{self.help}")
376
+
377
+ for name, cmd in self.commands.items():
378
+ if not cmd.get('hidden', False):
379
+ console.print(f" {name:20} {cmd['help'] or ''}")
380
+
381
+ console.print("\nUse -h or --help with any command for more info")
382
+
383
+ class CLI:
384
+ """
385
+ The main CLI application class that handles all command registration and execution.
386
+
387
+ Basic Usage:
388
+ >>> from swiftcli import CLI
389
+ >>> app = CLI("myapp")
390
+ >>> @app.command()
391
+ ... def greet(name: str):
392
+ ... print(f"Hello {name}!")
393
+ >>> app.run()
394
+
395
+ Advanced Usage:
396
+ >>> app = CLI("myapp", version="1.0.0")
397
+ >>> @app.group()
398
+ ... def config():
399
+ ... '''Manage configuration'''
400
+ ... pass
401
+ >>> @config.command()
402
+ ... def set(key: str, value: str):
403
+ ... '''Set config value'''
404
+ ... print(f"Setting {key}={value}")
405
+ """
406
+ def __init__(
407
+ self,
408
+ name: str = None,
409
+ help: str = None,
410
+ version: str = None,
411
+ chain: bool = False
412
+ ):
413
+ self.name = name
414
+ self.help = help
415
+ self.version = version
416
+ self.chain = chain
417
+ self.commands = {}
418
+ self.groups = {}
419
+ self.plugin_manager = PluginManager()
420
+
421
+ def command(
422
+ self,
423
+ name: str = None,
424
+ help: str = None,
425
+ aliases: List[str] = None,
426
+ hidden: bool = False
427
+ ):
428
+ """
429
+ Decorator to register a new command.
430
+
431
+ Basic Usage:
432
+ >>> @app.command()
433
+ ... def hello(name: str):
434
+ ... '''Say hello'''
435
+ ... print(f"Hello {name}!")
436
+
437
+ Advanced Usage:
438
+ >>> @app.command(name="greet", aliases=["hi", "hey"])
439
+ ... def hello(name: str):
440
+ ... '''Greet someone'''
441
+ ... print(f"Hello {name}!")
442
+ """
443
+ def decorator(f):
444
+ cmd_name = name or f.__name__
445
+ self.commands[cmd_name] = {
446
+ 'func': f,
447
+ 'help': help or f.__doc__,
448
+ 'aliases': aliases or [],
449
+ 'hidden': hidden
450
+ }
451
+ return f
452
+ return decorator
453
+
454
+ def group(
455
+ self,
456
+ name: str = None,
457
+ help: str = None,
458
+ chain: bool = False,
459
+ **kwargs
460
+ ):
461
+ """Create a command group"""
462
+ def decorator(f):
463
+ if hasattr(f, '_group'):
464
+ group_info = f._group
465
+ group = Group(
466
+ name=group_info['name'],
467
+ help=group_info['help'],
468
+ chain=group_info['chain'],
469
+ invoke_without_command=group_info['invoke_without_command']
470
+ )
471
+ else:
472
+ group = Group(
473
+ name=name or f.__name__,
474
+ help=help or f.__doc__,
475
+ chain=chain
476
+ )
477
+ self.groups[group.name] = group
478
+ return group
479
+ return decorator
480
+
481
+ def run(self, args: List[str] = None):
482
+ """Run the CLI application"""
483
+ args = args or sys.argv[1:]
484
+
485
+ if not args or args[0] in ['-h', '--help']:
486
+ self._print_help()
487
+ return
488
+
489
+ if args[0] in ['-v', '--version'] and self.version:
490
+ console.print(self.version)
491
+ return
492
+
493
+ command_name = args[0]
494
+ command_args = args[1:]
495
+
496
+ # Check if it's a group command
497
+ if command_name in self.groups:
498
+ group = self.groups[command_name]
499
+ if len(command_args) == 0:
500
+ if not group.invoke_without_command:
501
+ group._print_help()
502
+ return
503
+ else:
504
+ return group.run(command_args)
505
+
506
+ # Regular command
507
+ if command_name not in self.commands:
508
+ console.print(f"[red]Unknown command: {command_name}[/red]")
509
+ self._print_help()
510
+ return 1
511
+
512
+ command = self.commands[command_name]
513
+ try:
514
+ ctx = Context(self, command=command_name)
515
+ result = command['func'](**self._parse_args(command, command_args))
516
+
517
+ if self.chain and result is not None:
518
+ return result
519
+
520
+ except Exception as e:
521
+ console.print(f"[red]Error: {str(e)}[/red]")
522
+ return 1
523
+
524
+ def _parse_args(self, command: Dict, args: List[str]) -> Dict[str, Any]:
525
+ """Parse command arguments"""
526
+ params = {}
527
+ func = command['func']
528
+ sig = inspect.signature(func)
529
+
530
+ # Handle options
531
+ if hasattr(func, '_options'):
532
+ for opt in func._options:
533
+ # Get the destination parameter name from the longest option
534
+ param_decls = sorted(opt['param_decls'], key=len, reverse=True)
535
+ param_name = param_decls[0].lstrip('-').replace('-', '_')
536
+
537
+ # If there's a parameter name in the signature, use that instead
538
+ for param in sig.parameters.values():
539
+ if param.name in [d.lstrip('-').replace('-', '_') for d in param_decls]:
540
+ param_name = param.name
541
+ break
542
+
543
+ found = False
544
+ multiple_values = []
545
+
546
+ # Check for long and short options
547
+ i = 0
548
+ while i < len(args):
549
+ if args[i] in opt['param_decls']:
550
+ if opt.get('is_flag', False):
551
+ if opt.get('multiple', False):
552
+ multiple_values.append(True)
553
+ else:
554
+ params[param_name] = True
555
+ else:
556
+ if i + 1 < len(args):
557
+ value = args[i + 1]
558
+ # Convert value to the correct type
559
+ if 'type' in opt:
560
+ try:
561
+ value = opt['type'](value)
562
+ except ValueError:
563
+ raise UsageError(f"Invalid value for {args[i]}: {value}")
564
+
565
+ if opt.get('multiple', False):
566
+ multiple_values.append(value)
567
+ else:
568
+ params[param_name] = value
569
+ args.pop(i + 1)
570
+ else:
571
+ raise UsageError(f"Option {args[i]} requires a value")
572
+ args.pop(i)
573
+ found = True
574
+ if not opt.get('multiple', False):
575
+ break
576
+ else:
577
+ i += 1
578
+
579
+ # Set multiple values if any
580
+ if multiple_values:
581
+ params[param_name] = multiple_values
582
+
583
+ # Handle required options
584
+ if not found and opt.get('required', False):
585
+ raise UsageError(f"Option {opt['param_decls'][0]} is required")
586
+
587
+ # Set default value if not found
588
+ if not found and 'default' in opt:
589
+ params[param_name] = opt['default']
590
+
591
+ # Handle arguments
592
+ if hasattr(func, '_arguments'):
593
+ for i, arg in enumerate(func._arguments):
594
+ if i < len(args):
595
+ value = args[i]
596
+ # Convert value to the correct type
597
+ if 'type' in arg:
598
+ try:
599
+ value = arg['type'](value)
600
+ except ValueError:
601
+ raise UsageError(f"Invalid value for {arg['name']}: {value}")
602
+ params[arg['name']] = value
603
+ elif arg.get('required', True):
604
+ raise UsageError(f"Argument {arg['name']} is required")
605
+ elif 'default' in arg:
606
+ params[arg['name']] = arg['default']
607
+
608
+ # Handle environment variables
609
+ if hasattr(func, '_envvars'):
610
+ for env in func._envvars:
611
+ value = os.environ.get(env['name'])
612
+ if env.get('required', False) and not value:
613
+ raise UsageError(f"Environment variable {env['name']} is required")
614
+ if value:
615
+ # Convert value to the correct type
616
+ if 'type' in env:
617
+ try:
618
+ value = env['type'](value)
619
+ except ValueError:
620
+ raise UsageError(f"Invalid value for {env['name']}: {value}")
621
+ params[env['name'].lower()] = value
622
+
623
+ return params
624
+
625
+ def _print_help(self):
626
+ """Print main help message"""
627
+ console.print(f"\n{self.name or 'CLI Application'}")
628
+ if self.help:
629
+ console.print(f"\n{self.help}")
630
+
631
+ console.print("\nCommands:")
632
+ for name, cmd in self.commands.items():
633
+ if not cmd.get('hidden', False):
634
+ console.print(f" {name:20} {cmd['help'] or ''}")
635
+
636
+ for name, group in self.groups.items():
637
+ console.print(f"\n{name} commands:")
638
+ for cmd_name, cmd in group.commands.items():
639
+ if not cmd.get('hidden', False):
640
+ console.print(f" {name} {cmd_name:20} {cmd['help'] or ''}")
641
+
642
+ console.print("\nUse -h or --help with any command for more info")
643
+
644
+ def command(
645
+ name: str = None,
646
+ help: str = None,
647
+ aliases: List[str] = None,
648
+ hidden: bool = False
649
+ ):
650
+ """
651
+ Decorator to register a new command.
652
+
653
+ Basic Usage:
654
+ >>> @app.command()
655
+ ... def hello(name: str):
656
+ ... '''Say hello'''
657
+ ... print(f"Hello {name}!")
658
+
659
+ Advanced Usage:
660
+ >>> @app.command(name="greet", aliases=["hi", "hey"])
661
+ ... def hello(name: str):
662
+ ... '''Greet someone'''
663
+ ... print(f"Hello {name}!")
664
+ """
665
+ def decorator(f: Callable) -> Callable:
666
+ f._command = {
667
+ 'name': name or f.__name__,
668
+ 'help': help or f.__doc__,
669
+ 'aliases': aliases or [],
670
+ 'hidden': hidden
671
+ }
672
+ return f
673
+ return decorator
674
+
675
+ def option(*param_decls, **attrs):
676
+ """
677
+ Decorator to add an option to a command.
678
+
679
+ Basic Usage:
680
+ >>> @app.command()
681
+ ... @option("--count", type=int, default=1)
682
+ ... def repeat(count: int, message: str):
683
+ ... '''Repeat a message'''
684
+ ... for _ in range(count):
685
+ ... print(message)
686
+
687
+ Advanced Usage:
688
+ >>> @app.command()
689
+ ... @option("--format", "-f", type=click.Choice(["json", "yaml"]))
690
+ ... def export(format: str):
691
+ ... '''Export data'''
692
+ ... print(f"Exporting as {format}")
693
+ """
694
+ def decorator(f: Callable) -> Callable:
695
+ if not hasattr(f, '_options'):
696
+ f._options = []
697
+
698
+ # Set default values
699
+ attrs.setdefault('type', str)
700
+ attrs.setdefault('required', False)
701
+ attrs.setdefault('default', None)
702
+ attrs.setdefault('help', None)
703
+ attrs.setdefault('is_flag', False)
704
+ attrs.setdefault('multiple', False)
705
+ attrs.setdefault('count', False)
706
+ attrs.setdefault('prompt', False)
707
+ attrs.setdefault('hide_input', False)
708
+ attrs.setdefault('confirmation_prompt', False)
709
+ attrs.setdefault('choices', None)
710
+ attrs.setdefault('callback', None)
711
+ attrs.setdefault('show_default', True)
712
+ attrs.setdefault('hidden', False)
713
+
714
+ f._options.append({
715
+ 'param_decls': param_decls,
716
+ **attrs
717
+ })
718
+ return f
719
+ return decorator
720
+
721
+ def argument(name: str, **attrs):
722
+ """Argument decorator"""
723
+ def decorator(f: Callable) -> Callable:
724
+ if not hasattr(f, '_arguments'):
725
+ f._arguments = []
726
+ f._arguments.append({
727
+ 'name': name,
728
+ **attrs
729
+ })
730
+ return f
731
+ return decorator
732
+
733
+ def group(
734
+ name: str = None,
735
+ help: str = None,
736
+ chain: bool = False,
737
+ invoke_without_command: bool = False
738
+ ):
739
+ """Group decorator"""
740
+ def decorator(f: Callable) -> Callable:
741
+ f._group = {
742
+ 'name': name or f.__name__,
743
+ 'help': help or f.__doc__,
744
+ 'chain': chain,
745
+ 'invoke_without_command': invoke_without_command
746
+ }
747
+ return f
748
+ return decorator
749
+
750
+ def pass_context(f: Callable) -> Callable:
751
+ """Pass context decorator"""
752
+ f._pass_context = True
753
+ return f
754
+
755
+ def envvar(name: str, help: str = None, required: bool = False):
756
+ """Environment variable decorator"""
757
+ def decorator(f: Callable) -> Callable:
758
+ if not hasattr(f, '_envvars'):
759
+ f._envvars = []
760
+ f._envvars.append({
761
+ 'name': name,
762
+ 'help': help,
763
+ 'required': required
764
+ })
765
+ return f
766
+ return decorator
767
+
768
+ def config_file(path: str = None, auto_create: bool = True):
769
+ """Configuration file decorator"""
770
+ def decorator(f: Callable) -> Callable:
771
+ f._config = {
772
+ 'path': path,
773
+ 'auto_create': auto_create
774
+ }
775
+ return f
776
+ return decorator
777
+
778
+ def table_output(headers: List[str], style: str = None):
779
+ """Table output decorator"""
780
+ def decorator(f: Callable) -> Callable:
781
+ @wraps(f)
782
+ def wrapper(*args, **kwargs):
783
+ result = f(*args, **kwargs)
784
+ if result:
785
+ table = Table(show_header=True, header_style="bold blue")
786
+ for header in headers:
787
+ table.add_column(header)
788
+ for row in result:
789
+ table.add_row(*[str(cell) for cell in row])
790
+ console.print(table)
791
+ return result
792
+ return wrapper
793
+ return decorator
794
+
795
+ def progress(description: str = None):
796
+ """Progress decorator"""
797
+ def decorator(f: Callable) -> Callable:
798
+ @wraps(f)
799
+ def wrapper(*args, **kwargs):
800
+ with Progress(
801
+ SpinnerColumn(),
802
+ TextColumn("[progress.description]{task.description}"),
803
+ transient=True,
804
+ ) as progress:
805
+ task = progress.add_task(description or f.__name__, total=None)
806
+ result = f(*args, **kwargs)
807
+ progress.update(task, completed=True)
808
+ return result
809
+ return wrapper
810
+ return decorator