webscout 8.2.9__py3-none-any.whl → 2026.1.19__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.
- webscout/AIauto.py +524 -251
- webscout/AIbase.py +247 -319
- webscout/AIutel.py +68 -703
- webscout/Bard.py +1072 -1026
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/__init__.py +20 -12
- webscout/Extra/GitToolkit/gitapi/gist.py +142 -0
- webscout/Extra/GitToolkit/gitapi/organization.py +91 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +308 -195
- webscout/Extra/GitToolkit/gitapi/search.py +162 -0
- webscout/Extra/GitToolkit/gitapi/trending.py +236 -0
- webscout/Extra/GitToolkit/gitapi/user.py +128 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +82 -62
- webscout/Extra/YTToolkit/README.md +443 -375
- webscout/Extra/YTToolkit/YTdownloader.py +953 -957
- webscout/Extra/YTToolkit/__init__.py +3 -3
- webscout/Extra/YTToolkit/transcriber.py +595 -476
- webscout/Extra/YTToolkit/ytapi/README.md +230 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +22 -6
- webscout/Extra/YTToolkit/ytapi/captions.py +190 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +302 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +178 -118
- webscout/Extra/YTToolkit/ytapi/hashtag.py +120 -0
- webscout/Extra/YTToolkit/ytapi/https.py +89 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -59
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -8
- webscout/Extra/YTToolkit/ytapi/query.py +143 -40
- webscout/Extra/YTToolkit/ytapi/shorts.py +122 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +68 -63
- webscout/Extra/YTToolkit/ytapi/suggestions.py +97 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +66 -62
- webscout/Extra/YTToolkit/ytapi/video.py +403 -232
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -684
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +28 -28
- webscout/Extra/tempmail/async_utils.py +143 -141
- webscout/Extra/tempmail/base.py +172 -161
- webscout/Extra/tempmail/cli.py +191 -187
- webscout/Extra/tempmail/emailnator.py +88 -84
- webscout/Extra/tempmail/mail_tm.py +378 -361
- webscout/Extra/tempmail/temp_mail_io.py +304 -292
- webscout/Extra/weather.py +196 -194
- webscout/Extra/weather_ascii.py +17 -15
- webscout/Provider/AISEARCH/PERPLEXED_search.py +175 -0
- webscout/Provider/AISEARCH/Perplexity.py +292 -333
- webscout/Provider/AISEARCH/README.md +106 -279
- webscout/Provider/AISEARCH/__init__.py +16 -9
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +357 -410
- webscout/Provider/AISEARCH/monica_search.py +200 -220
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -255
- webscout/Provider/Algion.py +413 -0
- webscout/Provider/Andi.py +74 -69
- webscout/Provider/Apriel.py +313 -0
- webscout/Provider/Ayle.py +323 -0
- webscout/Provider/ChatSandbox.py +329 -342
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +467 -340
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +175 -169
- webscout/Provider/GithubChat.py +385 -369
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -801
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -375
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -291
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -384
- webscout/Provider/Netwrck.py +273 -270
- webscout/Provider/Nvidia.py +310 -0
- webscout/Provider/OPENAI/DeepAI.py +489 -0
- webscout/Provider/OPENAI/K2Think.py +423 -0
- webscout/Provider/OPENAI/PI.py +463 -0
- webscout/Provider/OPENAI/README.md +890 -952
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -357
- webscout/Provider/OPENAI/__init__.py +148 -40
- webscout/Provider/OPENAI/ai4chat.py +348 -293
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/{exachat.py → ayle.py} +365 -444
- webscout/Provider/OPENAI/base.py +253 -249
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +870 -556
- webscout/Provider/OPENAI/chatsandbox.py +233 -173
- webscout/Provider/OPENAI/deepinfra.py +403 -322
- webscout/Provider/OPENAI/e2b.py +2370 -1414
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +452 -417
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -364
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +333 -308
- webscout/Provider/OPENAI/huggingface.py +321 -0
- webscout/Provider/OPENAI/ibm.py +425 -0
- webscout/Provider/OPENAI/llmchat.py +253 -0
- webscout/Provider/OPENAI/llmchatco.py +378 -335
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +374 -357
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -287
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -172
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +305 -304
- webscout/Provider/OPENAI/textpollinations.py +370 -339
- webscout/Provider/OPENAI/toolbaz.py +375 -413
- webscout/Provider/OPENAI/typefully.py +419 -355
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -318
- webscout/Provider/OPENAI/wisecat.py +359 -387
- webscout/Provider/OPENAI/writecream.py +185 -163
- webscout/Provider/OPENAI/x0gpt.py +462 -365
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -429
- webscout/Provider/QwenLM.py +346 -254
- webscout/Provider/STT/__init__.py +28 -0
- webscout/Provider/STT/base.py +303 -0
- webscout/Provider/STT/elevenlabs.py +264 -0
- webscout/Provider/Sambanova.py +317 -0
- webscout/Provider/TTI/README.md +69 -82
- webscout/Provider/TTI/__init__.py +37 -7
- webscout/Provider/TTI/base.py +147 -64
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -201
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -221
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -11
- webscout/Provider/TTS/README.md +186 -192
- webscout/Provider/TTS/__init__.py +43 -10
- webscout/Provider/TTS/base.py +523 -159
- webscout/Provider/TTS/deepgram.py +286 -156
- webscout/Provider/TTS/elevenlabs.py +189 -111
- webscout/Provider/TTS/freetts.py +218 -0
- webscout/Provider/TTS/murfai.py +288 -113
- webscout/Provider/TTS/openai_fm.py +364 -129
- webscout/Provider/TTS/parler.py +203 -111
- webscout/Provider/TTS/qwen.py +334 -0
- webscout/Provider/TTS/sherpa.py +286 -0
- webscout/Provider/TTS/speechma.py +693 -580
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +331 -308
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -475
- webscout/Provider/TypliAI.py +311 -305
- webscout/Provider/UNFINISHED/ChatHub.py +219 -209
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +331 -326
- webscout/Provider/{GizAI.py → UNFINISHED/GizAI.py} +300 -295
- webscout/Provider/{Marcus.py → UNFINISHED/Marcus.py} +218 -198
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/{MCPCore.py → UNFINISHED/XenAI.py} +330 -315
- webscout/Provider/UNFINISHED/Youchat.py +347 -330
- webscout/Provider/UNFINISHED/aihumanizer.py +41 -0
- webscout/Provider/UNFINISHED/grammerchecker.py +37 -0
- webscout/Provider/UNFINISHED/liner.py +342 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +246 -263
- webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +231 -224
- webscout/Provider/WiseCat.py +256 -233
- webscout/Provider/WrDoChat.py +390 -370
- webscout/Provider/__init__.py +115 -174
- webscout/Provider/ai4chat.py +181 -174
- webscout/Provider/akashgpt.py +330 -335
- webscout/Provider/cerebras.py +397 -290
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -283
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -325
- webscout/Provider/llama3mitril.py +230 -215
- webscout/Provider/llmchat.py +308 -258
- webscout/Provider/llmchatco.py +321 -306
- webscout/Provider/meta.py +996 -801
- webscout/Provider/oivscode.py +332 -309
- webscout/Provider/searchchat.py +316 -292
- webscout/Provider/sonus.py +264 -258
- webscout/Provider/toolbaz.py +359 -353
- webscout/Provider/turboseek.py +332 -266
- webscout/Provider/typefully.py +262 -202
- webscout/Provider/x0gpt.py +332 -299
- webscout/__init__.py +31 -39
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -524
- webscout/client.py +1497 -70
- webscout/conversation.py +140 -436
- webscout/exceptions.py +383 -362
- webscout/litagent/__init__.py +29 -29
- webscout/litagent/agent.py +492 -455
- webscout/litagent/constants.py +60 -60
- webscout/models.py +505 -181
- webscout/optimizers.py +74 -420
- webscout/prompt_manager.py +376 -288
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -404
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -210
- webscout/scout/core/scout.py +800 -607
- webscout/scout/core/search_result.py +51 -96
- webscout/scout/core/text_analyzer.py +64 -63
- webscout/scout/core/text_utils.py +412 -277
- webscout/scout/core/web_analyzer.py +54 -52
- webscout/scout/element.py +872 -478
- webscout/scout/parsers/__init__.py +70 -69
- webscout/scout/parsers/html5lib_parser.py +182 -172
- webscout/scout/parsers/html_parser.py +238 -236
- webscout/scout/parsers/lxml_parser.py +203 -178
- webscout/scout/utils.py +38 -37
- webscout/search/__init__.py +47 -0
- webscout/search/base.py +201 -0
- webscout/search/bing_main.py +45 -0
- webscout/search/brave_main.py +92 -0
- webscout/search/duckduckgo_main.py +57 -0
- webscout/search/engines/__init__.py +127 -0
- webscout/search/engines/bing/__init__.py +15 -0
- webscout/search/engines/bing/base.py +35 -0
- webscout/search/engines/bing/images.py +114 -0
- webscout/search/engines/bing/news.py +96 -0
- webscout/search/engines/bing/suggestions.py +36 -0
- webscout/search/engines/bing/text.py +109 -0
- webscout/search/engines/brave/__init__.py +19 -0
- webscout/search/engines/brave/base.py +47 -0
- webscout/search/engines/brave/images.py +213 -0
- webscout/search/engines/brave/news.py +353 -0
- webscout/search/engines/brave/suggestions.py +318 -0
- webscout/search/engines/brave/text.py +167 -0
- webscout/search/engines/brave/videos.py +364 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +80 -0
- webscout/search/engines/duckduckgo/base.py +189 -0
- webscout/search/engines/duckduckgo/images.py +100 -0
- webscout/search/engines/duckduckgo/maps.py +183 -0
- webscout/search/engines/duckduckgo/news.py +70 -0
- webscout/search/engines/duckduckgo/suggestions.py +22 -0
- webscout/search/engines/duckduckgo/text.py +221 -0
- webscout/search/engines/duckduckgo/translate.py +48 -0
- webscout/search/engines/duckduckgo/videos.py +80 -0
- webscout/search/engines/duckduckgo/weather.py +84 -0
- webscout/search/engines/mojeek.py +61 -0
- webscout/search/engines/wikipedia.py +77 -0
- webscout/search/engines/yahoo/__init__.py +41 -0
- webscout/search/engines/yahoo/answers.py +19 -0
- webscout/search/engines/yahoo/base.py +34 -0
- webscout/search/engines/yahoo/images.py +323 -0
- webscout/search/engines/yahoo/maps.py +19 -0
- webscout/search/engines/yahoo/news.py +258 -0
- webscout/search/engines/yahoo/suggestions.py +140 -0
- webscout/search/engines/yahoo/text.py +273 -0
- webscout/search/engines/yahoo/translate.py +19 -0
- webscout/search/engines/yahoo/videos.py +302 -0
- webscout/search/engines/yahoo/weather.py +220 -0
- webscout/search/engines/yandex.py +67 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +34 -0
- webscout/search/engines/yep/images.py +101 -0
- webscout/search/engines/yep/suggestions.py +38 -0
- webscout/search/engines/yep/text.py +99 -0
- webscout/search/http_client.py +172 -0
- webscout/search/results.py +141 -0
- webscout/search/yahoo_main.py +57 -0
- webscout/search/yep_main.py +48 -0
- webscout/server/__init__.py +48 -0
- webscout/server/config.py +78 -0
- webscout/server/exceptions.py +69 -0
- webscout/server/providers.py +286 -0
- webscout/server/request_models.py +131 -0
- webscout/server/request_processing.py +404 -0
- webscout/server/routes.py +642 -0
- webscout/server/server.py +351 -0
- webscout/server/ui_templates.py +1171 -0
- webscout/swiftcli/__init__.py +79 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +574 -297
- webscout/swiftcli/core/context.py +98 -104
- webscout/swiftcli/core/group.py +268 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +243 -221
- webscout/swiftcli/decorators/options.py +247 -220
- webscout/swiftcli/decorators/output.py +392 -252
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +134 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +58 -59
- webscout/swiftcli/utils/formatting.py +251 -252
- webscout/swiftcli/utils/parsing.py +368 -267
- webscout/update_checker.py +280 -136
- webscout/utils.py +28 -14
- webscout/version.py +2 -1
- webscout/version.py.bak +3 -0
- webscout/zeroart/__init__.py +218 -135
- webscout/zeroart/base.py +70 -66
- webscout/zeroart/effects.py +155 -101
- webscout/zeroart/fonts.py +1799 -1239
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/entry_points.txt +1 -1
- webscout/DWEBS.py +0 -520
- webscout/Extra/Act.md +0 -309
- webscout/Extra/GitToolkit/gitapi/README.md +0 -110
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/Extra/gguf.md +0 -430
- webscout/Extra/weather.md +0 -281
- webscout/Litlogger/README.md +0 -10
- webscout/Litlogger/__init__.py +0 -15
- webscout/Litlogger/formats.py +0 -4
- webscout/Litlogger/handlers.py +0 -103
- webscout/Litlogger/levels.py +0 -13
- webscout/Litlogger/logger.py +0 -92
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/AISEARCH/felo_search.py +0 -202
- webscout/Provider/AISEARCH/genspark_search.py +0 -324
- webscout/Provider/AISEARCH/hika_search.py +0 -186
- webscout/Provider/AISEARCH/scira_search.py +0 -298
- webscout/Provider/Aitopia.py +0 -316
- webscout/Provider/AllenAI.py +0 -440
- webscout/Provider/Blackboxai.py +0 -791
- webscout/Provider/ChatGPTClone.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Cloudflare.py +0 -324
- webscout/Provider/ExaChat.py +0 -358
- webscout/Provider/Flowith.py +0 -217
- webscout/Provider/FreeGemini.py +0 -250
- webscout/Provider/Glider.py +0 -225
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -469
- webscout/Provider/Hunyuan.py +0 -283
- webscout/Provider/LambdaChat.py +0 -411
- webscout/Provider/Llama3.py +0 -259
- webscout/Provider/Nemotron.py +0 -218
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/BLACKBOXAI.py +0 -766
- webscout/Provider/OPENAI/Cloudflare.py +0 -378
- webscout/Provider/OPENAI/FreeGemini.py +0 -283
- webscout/Provider/OPENAI/NEMOTRON.py +0 -232
- webscout/Provider/OPENAI/Qwen3.py +0 -283
- webscout/Provider/OPENAI/api.py +0 -969
- webscout/Provider/OPENAI/c4ai.py +0 -373
- webscout/Provider/OPENAI/chatgptclone.py +0 -494
- webscout/Provider/OPENAI/copilot.py +0 -242
- webscout/Provider/OPENAI/flowith.py +0 -162
- webscout/Provider/OPENAI/freeaichat.py +0 -359
- webscout/Provider/OPENAI/mcpcore.py +0 -389
- webscout/Provider/OPENAI/multichat.py +0 -376
- webscout/Provider/OPENAI/opkfc.py +0 -496
- webscout/Provider/OPENAI/scirachat.py +0 -477
- webscout/Provider/OPENAI/standardinput.py +0 -433
- webscout/Provider/OPENAI/typegpt.py +0 -364
- webscout/Provider/OPENAI/uncovrAI.py +0 -463
- webscout/Provider/OPENAI/venice.py +0 -431
- webscout/Provider/OPENAI/yep.py +0 -382
- webscout/Provider/OpenGPT.py +0 -209
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -290
- webscout/Provider/TTI/aiarta.py +0 -365
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/fastflux.py +0 -200
- webscout/Provider/TTI/piclumen.py +0 -203
- webscout/Provider/TTI/pixelmuse.py +0 -225
- webscout/Provider/TTS/gesserit.py +0 -128
- webscout/Provider/TTS/sthir.py +0 -94
- webscout/Provider/TeachAnything.py +0 -229
- webscout/Provider/UNFINISHED/puterjs.py +0 -635
- webscout/Provider/UNFINISHED/test_lmarena.py +0 -119
- webscout/Provider/Venice.py +0 -258
- webscout/Provider/VercelAI.py +0 -253
- webscout/Provider/Writecream.py +0 -246
- webscout/Provider/WritingMate.py +0 -269
- webscout/Provider/asksteve.py +0 -220
- webscout/Provider/chatglm.py +0 -215
- webscout/Provider/copilot.py +0 -425
- webscout/Provider/freeaichat.py +0 -285
- webscout/Provider/granite.py +0 -235
- webscout/Provider/hermes.py +0 -266
- webscout/Provider/koala.py +0 -170
- webscout/Provider/lmarena.py +0 -198
- webscout/Provider/multichat.py +0 -364
- webscout/Provider/scira_chat.py +0 -299
- webscout/Provider/scnet.py +0 -243
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/typegpt.py +0 -289
- webscout/Provider/uncovr.py +0 -368
- webscout/Provider/yep.py +0 -389
- webscout/litagent/Readme.md +0 -276
- webscout/litprinter/__init__.py +0 -59
- webscout/swiftcli/Readme.md +0 -323
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1184
- webscout/webscout_search_async.py +0 -654
- webscout/yep_search.py +0 -347
- webscout/zeroart/README.md +0 -89
- webscout-8.2.9.dist-info/METADATA +0 -1033
- webscout-8.2.9.dist-info/RECORD +0 -289
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/top_level.txt +0 -0
|
@@ -1,267 +1,368 @@
|
|
|
1
|
-
"""Utility functions for parsing and validating command-line arguments."""
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import Any, Dict, List, Optional,
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
) ->
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Args:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
1
|
+
"""Utility functions for parsing and validating command-line arguments."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
|
8
|
+
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
from ..exceptions import BadParameter, UsageError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def parse_args(args: List[str]) -> Dict[str, Any]:
|
|
15
|
+
"""
|
|
16
|
+
Parse command line arguments into a dictionary.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
args: List of command line arguments
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Dictionary of parsed arguments
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
>>> parse_args(['--name', 'test', '--flag', '-n', '42'])
|
|
26
|
+
{'name': 'test', 'flag': True, 'n': '42'}
|
|
27
|
+
"""
|
|
28
|
+
parsed = {}
|
|
29
|
+
i = 0
|
|
30
|
+
while i < len(args):
|
|
31
|
+
arg = args[i]
|
|
32
|
+
|
|
33
|
+
# Handle flags/options
|
|
34
|
+
if arg.startswith('-'):
|
|
35
|
+
# Support --key=value or -k=value syntax
|
|
36
|
+
if '=' in arg:
|
|
37
|
+
key, value = arg.lstrip('-').split('=', 1)
|
|
38
|
+
key = key.replace('-', '_')
|
|
39
|
+
else:
|
|
40
|
+
key = arg.lstrip('-').replace('-', '_')
|
|
41
|
+
|
|
42
|
+
# Check if next arg is a value or another flag
|
|
43
|
+
if i + 1 >= len(args) or args[i + 1].startswith('-'):
|
|
44
|
+
value = True # Flag without value
|
|
45
|
+
else:
|
|
46
|
+
value = args[i + 1]
|
|
47
|
+
i += 1
|
|
48
|
+
|
|
49
|
+
# Support repeated flags/options by turning into lists
|
|
50
|
+
if key in parsed:
|
|
51
|
+
existing = parsed[key]
|
|
52
|
+
if isinstance(existing, list):
|
|
53
|
+
existing.append(value)
|
|
54
|
+
else:
|
|
55
|
+
parsed[key] = [existing, value]
|
|
56
|
+
else:
|
|
57
|
+
parsed[key] = value
|
|
58
|
+
else:
|
|
59
|
+
# Positional argument
|
|
60
|
+
pos_index = len([k for k in parsed.keys() if k.startswith('arg')])
|
|
61
|
+
parsed[f'arg{pos_index}'] = arg
|
|
62
|
+
|
|
63
|
+
i += 1
|
|
64
|
+
|
|
65
|
+
return parsed
|
|
66
|
+
|
|
67
|
+
def validate_required(
|
|
68
|
+
params: Dict[str, Any],
|
|
69
|
+
required: List[str]
|
|
70
|
+
) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Validate required parameters are present.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
params: Parameter dictionary
|
|
76
|
+
required: List of required parameter names
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
UsageError: If required parameter is missing
|
|
80
|
+
"""
|
|
81
|
+
missing = [p for p in required if p not in params]
|
|
82
|
+
if missing:
|
|
83
|
+
raise UsageError(f"Missing required parameters: {', '.join(missing)}")
|
|
84
|
+
|
|
85
|
+
def convert_type(
|
|
86
|
+
value: str,
|
|
87
|
+
type_: Type,
|
|
88
|
+
param_name: str
|
|
89
|
+
) -> Any:
|
|
90
|
+
"""
|
|
91
|
+
Convert string value to specified type.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
value: String value to convert
|
|
95
|
+
type_: Target type
|
|
96
|
+
param_name: Parameter name for error messages
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Converted value
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
BadParameter: If conversion fails
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
# Handle boolean conversion robustly when the input may already be a bool
|
|
106
|
+
if type_ is bool:
|
|
107
|
+
if isinstance(value, bool):
|
|
108
|
+
return value
|
|
109
|
+
if isinstance(value, (int, float)):
|
|
110
|
+
return bool(value)
|
|
111
|
+
if isinstance(value, str):
|
|
112
|
+
return value.lower() in ('true', 't', 'yes', 'y', '1')
|
|
113
|
+
return bool(value)
|
|
114
|
+
|
|
115
|
+
# If a list is provided and the target type is a collection type, return as-is
|
|
116
|
+
if isinstance(value, list) and getattr(type_, '__origin__', None) in (list,):
|
|
117
|
+
return value
|
|
118
|
+
|
|
119
|
+
# Attempt to construct the type normally (e.g., int('42'), Enum('val'), etc.)
|
|
120
|
+
return type_(value)
|
|
121
|
+
except (ValueError, TypeError):
|
|
122
|
+
raise BadParameter(
|
|
123
|
+
f"Invalid value for {param_name}: {value} (expected {type_.__name__})"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def validate_choice(
|
|
127
|
+
value: Any,
|
|
128
|
+
choices: List[Any],
|
|
129
|
+
param_name: str,
|
|
130
|
+
case_sensitive: bool = True
|
|
131
|
+
) -> None:
|
|
132
|
+
"""
|
|
133
|
+
Validate value is one of allowed choices.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
value: Value to validate
|
|
137
|
+
choices: List of allowed choices
|
|
138
|
+
param_name: Parameter name for error messages
|
|
139
|
+
case_sensitive: Whether to do case-sensitive comparison
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
BadParameter: If value not in choices
|
|
143
|
+
"""
|
|
144
|
+
if not case_sensitive and isinstance(value, str):
|
|
145
|
+
if value.lower() not in [str(c).lower() for c in choices]:
|
|
146
|
+
raise BadParameter(
|
|
147
|
+
f"Invalid choice for {param_name}: {value} "
|
|
148
|
+
f"(choose from {', '.join(str(c) for c in choices)})"
|
|
149
|
+
)
|
|
150
|
+
elif value not in choices:
|
|
151
|
+
raise BadParameter(
|
|
152
|
+
f"Invalid choice for {param_name}: {value} "
|
|
153
|
+
f"(choose from {', '.join(str(c) for c in choices)})"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def validate_argument(
|
|
157
|
+
value: str,
|
|
158
|
+
validation_rules: Dict[str, Any],
|
|
159
|
+
param_name: str
|
|
160
|
+
) -> str:
|
|
161
|
+
"""
|
|
162
|
+
Validate argument against validation rules.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
value: Argument value to validate
|
|
166
|
+
validation_rules: Dictionary of validation rules
|
|
167
|
+
param_name: Parameter name for error messages
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Validated value
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
BadParameter: If validation fails
|
|
174
|
+
"""
|
|
175
|
+
if not value and validation_rules.get('required', True):
|
|
176
|
+
raise BadParameter(f"Required argument {param_name} is empty")
|
|
177
|
+
|
|
178
|
+
if 'min_length' in validation_rules and len(value) < validation_rules['min_length']:
|
|
179
|
+
raise BadParameter(
|
|
180
|
+
f"Argument {param_name} too short (min {validation_rules['min_length']} characters)"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if 'max_length' in validation_rules and len(value) > validation_rules['max_length']:
|
|
184
|
+
raise BadParameter(
|
|
185
|
+
f"Argument {param_name} too long (max {validation_rules['max_length']} characters)"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if 'pattern' in validation_rules:
|
|
189
|
+
pattern = validation_rules['pattern']
|
|
190
|
+
if isinstance(pattern, str):
|
|
191
|
+
pattern = re.compile(pattern)
|
|
192
|
+
if not pattern.match(value):
|
|
193
|
+
raise BadParameter(
|
|
194
|
+
f"Argument {param_name} doesn't match pattern: {validation_rules.get('pattern', pattern.pattern)}"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if 'choices' in validation_rules:
|
|
198
|
+
validate_choice(value, validation_rules['choices'], param_name,
|
|
199
|
+
validation_rules.get('case_sensitive', True))
|
|
200
|
+
|
|
201
|
+
return value
|
|
202
|
+
|
|
203
|
+
def check_mutually_exclusive(
|
|
204
|
+
params: Dict[str, Any],
|
|
205
|
+
exclusive_groups: List[List[str]]
|
|
206
|
+
) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Check that mutually exclusive options are not used together.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
params: Dictionary of parsed parameters
|
|
212
|
+
exclusive_groups: List of option groups that are mutually exclusive
|
|
213
|
+
|
|
214
|
+
Raises:
|
|
215
|
+
UsageError: If mutually exclusive options are used together
|
|
216
|
+
"""
|
|
217
|
+
for group in exclusive_groups:
|
|
218
|
+
provided_options = [opt for opt in group if opt in params and params[opt] is not None]
|
|
219
|
+
if len(provided_options) > 1:
|
|
220
|
+
raise UsageError(
|
|
221
|
+
f"Options {', '.join(provided_options)} are mutually exclusive "
|
|
222
|
+
f"and cannot be used together"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def load_config_file(
|
|
226
|
+
path: Union[str, Path],
|
|
227
|
+
format: str = 'auto',
|
|
228
|
+
required: bool = True
|
|
229
|
+
) -> Dict[str, Any]:
|
|
230
|
+
"""
|
|
231
|
+
Load configuration from file.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
path: Path to config file
|
|
235
|
+
format: File format (json, yaml, or auto)
|
|
236
|
+
required: Whether file is required
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Configuration dictionary
|
|
240
|
+
|
|
241
|
+
Raises:
|
|
242
|
+
UsageError: If required file not found or invalid format
|
|
243
|
+
"""
|
|
244
|
+
path = Path(os.path.expanduser(path))
|
|
245
|
+
|
|
246
|
+
if not path.exists():
|
|
247
|
+
if required:
|
|
248
|
+
raise UsageError(f"Config file not found: {path}")
|
|
249
|
+
return {}
|
|
250
|
+
|
|
251
|
+
# Auto-detect format from extension
|
|
252
|
+
if format == 'auto':
|
|
253
|
+
format = path.suffix.lstrip('.').lower()
|
|
254
|
+
if format not in ('json', 'yaml', 'yml'):
|
|
255
|
+
raise UsageError(f"Unsupported config format: {format}")
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
with open(path) as f:
|
|
259
|
+
if format == 'json':
|
|
260
|
+
return json.load(f)
|
|
261
|
+
elif format in ('yaml', 'yml'):
|
|
262
|
+
return yaml.safe_load(f)
|
|
263
|
+
else:
|
|
264
|
+
raise UsageError(f"Unsupported config format: {format}")
|
|
265
|
+
except Exception as e:
|
|
266
|
+
raise UsageError(f"Error loading config file: {str(e)}")
|
|
267
|
+
|
|
268
|
+
def parse_key_value(
|
|
269
|
+
value: str,
|
|
270
|
+
separator: str = '='
|
|
271
|
+
) -> tuple:
|
|
272
|
+
"""
|
|
273
|
+
Parse key-value string.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
value: String in format "key=value"
|
|
277
|
+
separator: Key-value separator
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Tuple of (key, value)
|
|
281
|
+
|
|
282
|
+
Raises:
|
|
283
|
+
BadParameter: If string not in key=value format
|
|
284
|
+
"""
|
|
285
|
+
try:
|
|
286
|
+
key, value = value.split(separator, 1)
|
|
287
|
+
return key.strip(), value.strip()
|
|
288
|
+
except ValueError:
|
|
289
|
+
raise BadParameter(
|
|
290
|
+
f"Invalid key-value pair: {value} (expected format: key{separator}value)"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def parse_list(
|
|
294
|
+
value: str,
|
|
295
|
+
separator: str = ','
|
|
296
|
+
) -> List[str]:
|
|
297
|
+
"""
|
|
298
|
+
Parse comma-separated list.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
value: Comma-separated string
|
|
302
|
+
separator: List item separator
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
List of strings
|
|
306
|
+
"""
|
|
307
|
+
return [x.strip() for x in value.split(separator) if x.strip()]
|
|
308
|
+
|
|
309
|
+
def parse_dict(
|
|
310
|
+
value: str,
|
|
311
|
+
item_separator: str = ',',
|
|
312
|
+
key_value_separator: str = '='
|
|
313
|
+
) -> Dict[str, str]:
|
|
314
|
+
"""
|
|
315
|
+
Parse dictionary string.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
value: String in format "key1=value1,key2=value2"
|
|
319
|
+
item_separator: Separator between items
|
|
320
|
+
key_value_separator: Separator between keys and values
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Dictionary of key-value pairs
|
|
324
|
+
|
|
325
|
+
Example:
|
|
326
|
+
>>> parse_dict("name=test,count=42")
|
|
327
|
+
{'name': 'test', 'count': '42'}
|
|
328
|
+
"""
|
|
329
|
+
result = {}
|
|
330
|
+
if not value:
|
|
331
|
+
return result
|
|
332
|
+
|
|
333
|
+
items = parse_list(value, item_separator)
|
|
334
|
+
for item in items:
|
|
335
|
+
key, value = parse_key_value(item, key_value_separator)
|
|
336
|
+
result[key] = value
|
|
337
|
+
|
|
338
|
+
return result
|
|
339
|
+
|
|
340
|
+
def get_env_var(
|
|
341
|
+
name: str,
|
|
342
|
+
type_: Type = str,
|
|
343
|
+
required: bool = False,
|
|
344
|
+
default: Any = None
|
|
345
|
+
) -> Any:
|
|
346
|
+
"""
|
|
347
|
+
Get and validate environment variable.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
name: Environment variable name
|
|
351
|
+
type_: Expected type
|
|
352
|
+
required: Whether variable is required
|
|
353
|
+
default: Default value if not set
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Environment variable value
|
|
357
|
+
|
|
358
|
+
Raises:
|
|
359
|
+
UsageError: If required variable not set
|
|
360
|
+
"""
|
|
361
|
+
value = os.environ.get(name)
|
|
362
|
+
|
|
363
|
+
if value is None:
|
|
364
|
+
if required:
|
|
365
|
+
raise UsageError(f"Required environment variable not set: {name}")
|
|
366
|
+
return default
|
|
367
|
+
|
|
368
|
+
return convert_type(value, type_, name)
|