webscout 8.2.7__py3-none-any.whl → 8.2.9__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.
Files changed (281) hide show
  1. webscout/AIauto.py +33 -15
  2. webscout/AIbase.py +96 -37
  3. webscout/AIutel.py +703 -250
  4. webscout/Bard.py +441 -323
  5. webscout/Extra/Act.md +309 -0
  6. webscout/Extra/GitToolkit/__init__.py +10 -0
  7. webscout/Extra/GitToolkit/gitapi/README.md +110 -0
  8. webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
  9. webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
  10. webscout/Extra/GitToolkit/gitapi/user.py +96 -0
  11. webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
  12. webscout/Extra/YTToolkit/README.md +375 -0
  13. webscout/Extra/YTToolkit/YTdownloader.py +957 -0
  14. webscout/Extra/YTToolkit/__init__.py +3 -0
  15. webscout/Extra/YTToolkit/transcriber.py +476 -0
  16. webscout/Extra/YTToolkit/ytapi/README.md +44 -0
  17. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  18. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  19. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  20. webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
  21. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  22. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  23. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  24. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  25. webscout/Extra/YTToolkit/ytapi/query.py +40 -0
  26. webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
  27. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  28. webscout/Extra/YTToolkit/ytapi/video.py +232 -0
  29. webscout/Extra/__init__.py +7 -0
  30. webscout/Extra/autocoder/__init__.py +9 -0
  31. webscout/Extra/autocoder/autocoder.py +1105 -0
  32. webscout/Extra/autocoder/autocoder_utiles.py +332 -0
  33. webscout/Extra/gguf.md +430 -0
  34. webscout/Extra/gguf.py +684 -0
  35. webscout/Extra/tempmail/README.md +488 -0
  36. webscout/Extra/tempmail/__init__.py +28 -0
  37. webscout/Extra/tempmail/async_utils.py +141 -0
  38. webscout/Extra/tempmail/base.py +161 -0
  39. webscout/Extra/tempmail/cli.py +187 -0
  40. webscout/Extra/tempmail/emailnator.py +84 -0
  41. webscout/Extra/tempmail/mail_tm.py +361 -0
  42. webscout/Extra/tempmail/temp_mail_io.py +292 -0
  43. webscout/Extra/weather.md +281 -0
  44. webscout/Extra/weather.py +194 -0
  45. webscout/Extra/weather_ascii.py +76 -0
  46. webscout/Litlogger/README.md +10 -0
  47. webscout/Litlogger/__init__.py +15 -0
  48. webscout/Litlogger/formats.py +4 -0
  49. webscout/Litlogger/handlers.py +103 -0
  50. webscout/Litlogger/levels.py +13 -0
  51. webscout/Litlogger/logger.py +92 -0
  52. webscout/Provider/AI21.py +177 -0
  53. webscout/Provider/AISEARCH/DeepFind.py +254 -0
  54. webscout/Provider/AISEARCH/Perplexity.py +333 -0
  55. webscout/Provider/AISEARCH/README.md +279 -0
  56. webscout/Provider/AISEARCH/__init__.py +9 -0
  57. webscout/Provider/AISEARCH/felo_search.py +202 -0
  58. webscout/Provider/AISEARCH/genspark_search.py +324 -0
  59. webscout/Provider/AISEARCH/hika_search.py +186 -0
  60. webscout/Provider/AISEARCH/iask_search.py +410 -0
  61. webscout/Provider/AISEARCH/monica_search.py +220 -0
  62. webscout/Provider/AISEARCH/scira_search.py +298 -0
  63. webscout/Provider/AISEARCH/webpilotai_search.py +255 -0
  64. webscout/Provider/Aitopia.py +316 -0
  65. webscout/Provider/AllenAI.py +440 -0
  66. webscout/Provider/Andi.py +228 -0
  67. webscout/Provider/Blackboxai.py +791 -0
  68. webscout/Provider/ChatGPTClone.py +237 -0
  69. webscout/Provider/ChatGPTGratis.py +194 -0
  70. webscout/Provider/ChatSandbox.py +342 -0
  71. webscout/Provider/Cloudflare.py +324 -0
  72. webscout/Provider/Cohere.py +208 -0
  73. webscout/Provider/Deepinfra.py +340 -0
  74. webscout/Provider/ExaAI.py +261 -0
  75. webscout/Provider/ExaChat.py +358 -0
  76. webscout/Provider/Flowith.py +217 -0
  77. webscout/Provider/FreeGemini.py +250 -0
  78. webscout/Provider/Gemini.py +169 -0
  79. webscout/Provider/GithubChat.py +369 -0
  80. webscout/Provider/GizAI.py +295 -0
  81. webscout/Provider/Glider.py +225 -0
  82. webscout/Provider/Groq.py +801 -0
  83. webscout/Provider/HF_space/__init__.py +0 -0
  84. webscout/Provider/HF_space/qwen_qwen2.py +206 -0
  85. webscout/Provider/HeckAI.py +375 -0
  86. webscout/Provider/HuggingFaceChat.py +469 -0
  87. webscout/Provider/Hunyuan.py +283 -0
  88. webscout/Provider/Jadve.py +291 -0
  89. webscout/Provider/Koboldai.py +384 -0
  90. webscout/Provider/LambdaChat.py +411 -0
  91. webscout/Provider/Llama3.py +259 -0
  92. webscout/Provider/MCPCore.py +315 -0
  93. webscout/Provider/Marcus.py +198 -0
  94. webscout/Provider/Nemotron.py +218 -0
  95. webscout/Provider/Netwrck.py +270 -0
  96. webscout/Provider/OLLAMA.py +396 -0
  97. webscout/Provider/OPENAI/BLACKBOXAI.py +766 -0
  98. webscout/Provider/OPENAI/Cloudflare.py +378 -0
  99. webscout/Provider/OPENAI/FreeGemini.py +283 -0
  100. webscout/Provider/OPENAI/NEMOTRON.py +232 -0
  101. webscout/Provider/OPENAI/Qwen3.py +283 -0
  102. webscout/Provider/OPENAI/README.md +952 -0
  103. webscout/Provider/OPENAI/TwoAI.py +357 -0
  104. webscout/Provider/OPENAI/__init__.py +40 -0
  105. webscout/Provider/OPENAI/ai4chat.py +293 -0
  106. webscout/Provider/OPENAI/api.py +969 -0
  107. webscout/Provider/OPENAI/base.py +249 -0
  108. webscout/Provider/OPENAI/c4ai.py +373 -0
  109. webscout/Provider/OPENAI/chatgpt.py +556 -0
  110. webscout/Provider/OPENAI/chatgptclone.py +494 -0
  111. webscout/Provider/OPENAI/chatsandbox.py +173 -0
  112. webscout/Provider/OPENAI/copilot.py +242 -0
  113. webscout/Provider/OPENAI/deepinfra.py +322 -0
  114. webscout/Provider/OPENAI/e2b.py +1414 -0
  115. webscout/Provider/OPENAI/exaai.py +417 -0
  116. webscout/Provider/OPENAI/exachat.py +444 -0
  117. webscout/Provider/OPENAI/flowith.py +162 -0
  118. webscout/Provider/OPENAI/freeaichat.py +359 -0
  119. webscout/Provider/OPENAI/glider.py +326 -0
  120. webscout/Provider/OPENAI/groq.py +364 -0
  121. webscout/Provider/OPENAI/heckai.py +308 -0
  122. webscout/Provider/OPENAI/llmchatco.py +335 -0
  123. webscout/Provider/OPENAI/mcpcore.py +389 -0
  124. webscout/Provider/OPENAI/multichat.py +376 -0
  125. webscout/Provider/OPENAI/netwrck.py +357 -0
  126. webscout/Provider/OPENAI/oivscode.py +287 -0
  127. webscout/Provider/OPENAI/opkfc.py +496 -0
  128. webscout/Provider/OPENAI/pydantic_imports.py +172 -0
  129. webscout/Provider/OPENAI/scirachat.py +477 -0
  130. webscout/Provider/OPENAI/sonus.py +304 -0
  131. webscout/Provider/OPENAI/standardinput.py +433 -0
  132. webscout/Provider/OPENAI/textpollinations.py +339 -0
  133. webscout/Provider/OPENAI/toolbaz.py +413 -0
  134. webscout/Provider/OPENAI/typefully.py +355 -0
  135. webscout/Provider/OPENAI/typegpt.py +364 -0
  136. webscout/Provider/OPENAI/uncovrAI.py +463 -0
  137. webscout/Provider/OPENAI/utils.py +318 -0
  138. webscout/Provider/OPENAI/venice.py +431 -0
  139. webscout/Provider/OPENAI/wisecat.py +387 -0
  140. webscout/Provider/OPENAI/writecream.py +163 -0
  141. webscout/Provider/OPENAI/x0gpt.py +365 -0
  142. webscout/Provider/OPENAI/yep.py +382 -0
  143. webscout/Provider/OpenGPT.py +209 -0
  144. webscout/Provider/Openai.py +496 -0
  145. webscout/Provider/PI.py +429 -0
  146. webscout/Provider/Perplexitylabs.py +415 -0
  147. webscout/Provider/QwenLM.py +254 -0
  148. webscout/Provider/Reka.py +214 -0
  149. webscout/Provider/StandardInput.py +290 -0
  150. webscout/Provider/TTI/README.md +82 -0
  151. webscout/Provider/TTI/__init__.py +7 -0
  152. webscout/Provider/TTI/aiarta.py +365 -0
  153. webscout/Provider/TTI/artbit.py +0 -0
  154. webscout/Provider/TTI/base.py +64 -0
  155. webscout/Provider/TTI/fastflux.py +200 -0
  156. webscout/Provider/TTI/magicstudio.py +201 -0
  157. webscout/Provider/TTI/piclumen.py +203 -0
  158. webscout/Provider/TTI/pixelmuse.py +225 -0
  159. webscout/Provider/TTI/pollinations.py +221 -0
  160. webscout/Provider/TTI/utils.py +11 -0
  161. webscout/Provider/TTS/README.md +192 -0
  162. webscout/Provider/TTS/__init__.py +10 -0
  163. webscout/Provider/TTS/base.py +159 -0
  164. webscout/Provider/TTS/deepgram.py +156 -0
  165. webscout/Provider/TTS/elevenlabs.py +111 -0
  166. webscout/Provider/TTS/gesserit.py +128 -0
  167. webscout/Provider/TTS/murfai.py +113 -0
  168. webscout/Provider/TTS/openai_fm.py +129 -0
  169. webscout/Provider/TTS/parler.py +111 -0
  170. webscout/Provider/TTS/speechma.py +580 -0
  171. webscout/Provider/TTS/sthir.py +94 -0
  172. webscout/Provider/TTS/streamElements.py +333 -0
  173. webscout/Provider/TTS/utils.py +280 -0
  174. webscout/Provider/TeachAnything.py +229 -0
  175. webscout/Provider/TextPollinationsAI.py +308 -0
  176. webscout/Provider/TwoAI.py +475 -0
  177. webscout/Provider/TypliAI.py +305 -0
  178. webscout/Provider/UNFINISHED/ChatHub.py +209 -0
  179. webscout/Provider/UNFINISHED/Youchat.py +330 -0
  180. webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
  181. webscout/Provider/UNFINISHED/puterjs.py +635 -0
  182. webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
  183. webscout/Provider/Venice.py +258 -0
  184. webscout/Provider/VercelAI.py +253 -0
  185. webscout/Provider/WiseCat.py +233 -0
  186. webscout/Provider/WrDoChat.py +370 -0
  187. webscout/Provider/Writecream.py +246 -0
  188. webscout/Provider/WritingMate.py +269 -0
  189. webscout/Provider/__init__.py +174 -0
  190. webscout/Provider/ai4chat.py +174 -0
  191. webscout/Provider/akashgpt.py +335 -0
  192. webscout/Provider/asksteve.py +220 -0
  193. webscout/Provider/cerebras.py +290 -0
  194. webscout/Provider/chatglm.py +215 -0
  195. webscout/Provider/cleeai.py +213 -0
  196. webscout/Provider/copilot.py +425 -0
  197. webscout/Provider/elmo.py +283 -0
  198. webscout/Provider/freeaichat.py +285 -0
  199. webscout/Provider/geminiapi.py +208 -0
  200. webscout/Provider/granite.py +235 -0
  201. webscout/Provider/hermes.py +266 -0
  202. webscout/Provider/julius.py +223 -0
  203. webscout/Provider/koala.py +170 -0
  204. webscout/Provider/learnfastai.py +325 -0
  205. webscout/Provider/llama3mitril.py +215 -0
  206. webscout/Provider/llmchat.py +258 -0
  207. webscout/Provider/llmchatco.py +306 -0
  208. webscout/Provider/lmarena.py +198 -0
  209. webscout/Provider/meta.py +801 -0
  210. webscout/Provider/multichat.py +364 -0
  211. webscout/Provider/oivscode.py +309 -0
  212. webscout/Provider/samurai.py +224 -0
  213. webscout/Provider/scira_chat.py +299 -0
  214. webscout/Provider/scnet.py +243 -0
  215. webscout/Provider/searchchat.py +292 -0
  216. webscout/Provider/sonus.py +258 -0
  217. webscout/Provider/talkai.py +194 -0
  218. webscout/Provider/toolbaz.py +353 -0
  219. webscout/Provider/turboseek.py +266 -0
  220. webscout/Provider/typefully.py +202 -0
  221. webscout/Provider/typegpt.py +289 -0
  222. webscout/Provider/uncovr.py +368 -0
  223. webscout/Provider/x0gpt.py +299 -0
  224. webscout/Provider/yep.py +389 -0
  225. webscout/__init__.py +4 -2
  226. webscout/cli.py +3 -28
  227. webscout/client.py +70 -0
  228. webscout/conversation.py +35 -35
  229. webscout/litagent/Readme.md +276 -0
  230. webscout/litagent/__init__.py +29 -0
  231. webscout/litagent/agent.py +455 -0
  232. webscout/litagent/constants.py +60 -0
  233. webscout/litprinter/__init__.py +59 -0
  234. webscout/optimizers.py +419 -419
  235. webscout/scout/README.md +404 -0
  236. webscout/scout/__init__.py +8 -0
  237. webscout/scout/core/__init__.py +7 -0
  238. webscout/scout/core/crawler.py +210 -0
  239. webscout/scout/core/scout.py +607 -0
  240. webscout/scout/core/search_result.py +96 -0
  241. webscout/scout/core/text_analyzer.py +63 -0
  242. webscout/scout/core/text_utils.py +277 -0
  243. webscout/scout/core/web_analyzer.py +52 -0
  244. webscout/scout/element.py +478 -0
  245. webscout/scout/parsers/__init__.py +69 -0
  246. webscout/scout/parsers/html5lib_parser.py +172 -0
  247. webscout/scout/parsers/html_parser.py +236 -0
  248. webscout/scout/parsers/lxml_parser.py +178 -0
  249. webscout/scout/utils.py +37 -0
  250. webscout/swiftcli/Readme.md +323 -0
  251. webscout/swiftcli/__init__.py +95 -0
  252. webscout/swiftcli/core/__init__.py +7 -0
  253. webscout/swiftcli/core/cli.py +297 -0
  254. webscout/swiftcli/core/context.py +104 -0
  255. webscout/swiftcli/core/group.py +241 -0
  256. webscout/swiftcli/decorators/__init__.py +28 -0
  257. webscout/swiftcli/decorators/command.py +221 -0
  258. webscout/swiftcli/decorators/options.py +220 -0
  259. webscout/swiftcli/decorators/output.py +252 -0
  260. webscout/swiftcli/exceptions.py +21 -0
  261. webscout/swiftcli/plugins/__init__.py +9 -0
  262. webscout/swiftcli/plugins/base.py +135 -0
  263. webscout/swiftcli/plugins/manager.py +269 -0
  264. webscout/swiftcli/utils/__init__.py +59 -0
  265. webscout/swiftcli/utils/formatting.py +252 -0
  266. webscout/swiftcli/utils/parsing.py +267 -0
  267. webscout/version.py +1 -1
  268. webscout/webscout_search.py +2 -182
  269. webscout/webscout_search_async.py +1 -179
  270. webscout/zeroart/README.md +89 -0
  271. webscout/zeroart/__init__.py +135 -0
  272. webscout/zeroart/base.py +66 -0
  273. webscout/zeroart/effects.py +101 -0
  274. webscout/zeroart/fonts.py +1239 -0
  275. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/METADATA +262 -83
  276. webscout-8.2.9.dist-info/RECORD +289 -0
  277. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/WHEEL +1 -1
  278. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/entry_points.txt +1 -0
  279. webscout-8.2.7.dist-info/RECORD +0 -26
  280. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/licenses/LICENSE.md +0 -0
  281. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,252 @@
1
+ """Utility functions for text formatting and styling."""
2
+
3
+ import re
4
+ from typing import Any, Dict, List, Optional, Union
5
+
6
+ from rich.console import Console
7
+ from rich.style import Style
8
+ from rich.text import Text
9
+ from rich.table import Table
10
+ from rich.padding import Padding
11
+
12
+ console = Console()
13
+
14
+ def style_text(
15
+ text: str,
16
+ color: Optional[str] = None,
17
+ bold: bool = False,
18
+ italic: bool = False,
19
+ underline: bool = False
20
+ ) -> Text:
21
+ """
22
+ Apply styling to text.
23
+
24
+ Args:
25
+ text: Text to style
26
+ color: Text color
27
+ bold: Bold text
28
+ italic: Italic text
29
+ underline: Underline text
30
+
31
+ Returns:
32
+ Rich Text object with applied styling
33
+ """
34
+ style = []
35
+ if color:
36
+ style.append(color)
37
+ if bold:
38
+ style.append("bold")
39
+ if italic:
40
+ style.append("italic")
41
+ if underline:
42
+ style.append("underline")
43
+
44
+ return Text(text, style=" ".join(style))
45
+
46
+ def format_error(message: str, title: str = "Error") -> None:
47
+ """
48
+ Format and display error message.
49
+
50
+ Args:
51
+ message: Error message
52
+ title: Error title
53
+ """
54
+ console.print(f"[bold red]{title}:[/] {message}")
55
+
56
+ def format_warning(message: str, title: str = "Warning") -> None:
57
+ """
58
+ Format and display warning message.
59
+
60
+ Args:
61
+ message: Warning message
62
+ title: Warning title
63
+ """
64
+ console.print(f"[bold yellow]{title}:[/] {message}")
65
+
66
+ def format_success(message: str, title: str = "Success") -> None:
67
+ """
68
+ Format and display success message.
69
+
70
+ Args:
71
+ message: Success message
72
+ title: Success title
73
+ """
74
+ console.print(f"[bold green]{title}:[/] {message}")
75
+
76
+ def format_info(message: str, title: str = "Info") -> None:
77
+ """
78
+ Format and display info message.
79
+
80
+ Args:
81
+ message: Info message
82
+ title: Info title
83
+ """
84
+ console.print(f"[bold blue]{title}:[/] {message}")
85
+
86
+ def create_table(
87
+ headers: List[str],
88
+ rows: List[List[Any]],
89
+ title: Optional[str] = None,
90
+ style: str = "default",
91
+ show_lines: bool = False
92
+ ) -> Table:
93
+ """
94
+ Create a formatted table.
95
+
96
+ Args:
97
+ headers: Column headers
98
+ rows: Table rows
99
+ title: Table title
100
+ style: Table style
101
+ show_lines: Show row/column lines
102
+
103
+ Returns:
104
+ Rich Table object
105
+ """
106
+ table = Table(
107
+ title=title,
108
+ show_header=True,
109
+ header_style="bold blue",
110
+ show_lines=show_lines
111
+ )
112
+
113
+ # Add columns
114
+ for header in headers:
115
+ table.add_column(header)
116
+
117
+ # Add rows
118
+ for row in rows:
119
+ table.add_row(*[str(cell) for cell in row])
120
+
121
+ return table
122
+
123
+ def truncate_text(
124
+ text: str,
125
+ max_length: int,
126
+ suffix: str = "..."
127
+ ) -> str:
128
+ """
129
+ Truncate text to specified length.
130
+
131
+ Args:
132
+ text: Text to truncate
133
+ max_length: Maximum length
134
+ suffix: Truncation suffix
135
+
136
+ Returns:
137
+ Truncated text
138
+ """
139
+ if len(text) <= max_length:
140
+ return text
141
+ return text[:max_length - len(suffix)] + suffix
142
+
143
+ def wrap_text(
144
+ text: str,
145
+ width: int,
146
+ indent: str = "",
147
+ initial_indent: str = ""
148
+ ) -> str:
149
+ """
150
+ Wrap text to specified width.
151
+
152
+ Args:
153
+ text: Text to wrap
154
+ width: Maximum line width
155
+ indent: Indentation for wrapped lines
156
+ initial_indent: Indentation for first line
157
+
158
+ Returns:
159
+ Wrapped text
160
+ """
161
+ import textwrap
162
+ return textwrap.fill(
163
+ text,
164
+ width=width,
165
+ initial_indent=initial_indent,
166
+ subsequent_indent=indent
167
+ )
168
+
169
+ def format_dict(
170
+ data: Dict[str, Any],
171
+ indent: int = 2,
172
+ sort_keys: bool = True
173
+ ) -> str:
174
+ """
175
+ Format dictionary for display.
176
+
177
+ Args:
178
+ data: Dictionary to format
179
+ indent: Indentation level
180
+ sort_keys: Sort dictionary keys
181
+
182
+ Returns:
183
+ Formatted string
184
+ """
185
+ import json
186
+ return json.dumps(
187
+ data,
188
+ indent=indent,
189
+ sort_keys=sort_keys,
190
+ default=str
191
+ )
192
+
193
+ def format_list(
194
+ items: List[Any],
195
+ bullet: str = "•",
196
+ indent: int = 2
197
+ ) -> str:
198
+ """
199
+ Format list for display.
200
+
201
+ Args:
202
+ items: List to format
203
+ bullet: Bullet point character
204
+ indent: Indentation level
205
+
206
+ Returns:
207
+ Formatted string
208
+ """
209
+ indent_str = " " * indent
210
+ return "\n".join(f"{indent_str}{bullet} {item}" for item in items)
211
+
212
+ def strip_ansi(text: str) -> str:
213
+ """
214
+ Remove ANSI escape sequences from text.
215
+
216
+ Args:
217
+ text: Text containing ANSI sequences
218
+
219
+ Returns:
220
+ Clean text
221
+ """
222
+ ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
223
+ return ansi_escape.sub('', text)
224
+
225
+ def get_terminal_size() -> tuple:
226
+ """
227
+ Get terminal size.
228
+
229
+ Returns:
230
+ Tuple of (width, height)
231
+ """
232
+ return console.size
233
+
234
+ def clear_screen() -> None:
235
+ """Clear the terminal screen."""
236
+ console.clear()
237
+
238
+ def create_padding(
239
+ renderable: Any,
240
+ pad: Union[int, tuple] = 1
241
+ ) -> Padding:
242
+ """
243
+ Add padding around content.
244
+
245
+ Args:
246
+ renderable: Content to pad
247
+ pad: Padding amount
248
+
249
+ Returns:
250
+ Padded content
251
+ """
252
+ return Padding(renderable, pad)
@@ -0,0 +1,267 @@
1
+ """Utility functions for parsing and validating command-line arguments."""
2
+
3
+ import os
4
+ import json
5
+ import yaml
6
+ from pathlib import Path
7
+ from typing import Any, Dict, List, Optional, Union, Type
8
+
9
+ from ..exceptions import BadParameter, UsageError
10
+
11
+ def parse_args(args: List[str]) -> Dict[str, Any]:
12
+ """
13
+ Parse command line arguments into a dictionary.
14
+
15
+ Args:
16
+ args: List of command line arguments
17
+
18
+ Returns:
19
+ Dictionary of parsed arguments
20
+
21
+ Example:
22
+ >>> parse_args(['--name', 'test', '--flag', '-n', '42'])
23
+ {'name': 'test', 'flag': True, 'n': '42'}
24
+ """
25
+ parsed = {}
26
+ i = 0
27
+ while i < len(args):
28
+ arg = args[i]
29
+
30
+ # Handle flags/options
31
+ if arg.startswith('-'):
32
+ key = arg.lstrip('-').replace('-', '_')
33
+
34
+ # Check if next arg is a value or another flag
35
+ if i + 1 >= len(args) or args[i + 1].startswith('-'):
36
+ parsed[key] = True # Flag without value
37
+ else:
38
+ parsed[key] = args[i + 1]
39
+ i += 1
40
+ else:
41
+ # Positional argument
42
+ parsed[f'arg{len([k for k in parsed.keys() if k.startswith("arg")])}'] = arg
43
+
44
+ i += 1
45
+
46
+ return parsed
47
+
48
+ def validate_required(
49
+ params: Dict[str, Any],
50
+ required: List[str]
51
+ ) -> None:
52
+ """
53
+ Validate required parameters are present.
54
+
55
+ Args:
56
+ params: Parameter dictionary
57
+ required: List of required parameter names
58
+
59
+ Raises:
60
+ UsageError: If required parameter is missing
61
+ """
62
+ missing = [p for p in required if p not in params]
63
+ if missing:
64
+ raise UsageError(f"Missing required parameters: {', '.join(missing)}")
65
+
66
+ def convert_type(
67
+ value: str,
68
+ type_: Type,
69
+ param_name: str
70
+ ) -> Any:
71
+ """
72
+ Convert string value to specified type.
73
+
74
+ Args:
75
+ value: String value to convert
76
+ type_: Target type
77
+ param_name: Parameter name for error messages
78
+
79
+ Returns:
80
+ Converted value
81
+
82
+ Raises:
83
+ BadParameter: If conversion fails
84
+ """
85
+ try:
86
+ if type_ == bool:
87
+ return value.lower() in ('true', 't', 'yes', 'y', '1')
88
+ return type_(value)
89
+ except (ValueError, TypeError):
90
+ raise BadParameter(
91
+ f"Invalid value for {param_name}: {value} (expected {type_.__name__})"
92
+ )
93
+
94
+ def validate_choice(
95
+ value: Any,
96
+ choices: List[Any],
97
+ param_name: str,
98
+ case_sensitive: bool = True
99
+ ) -> None:
100
+ """
101
+ Validate value is one of allowed choices.
102
+
103
+ Args:
104
+ value: Value to validate
105
+ choices: List of allowed choices
106
+ param_name: Parameter name for error messages
107
+ case_sensitive: Whether to do case-sensitive comparison
108
+
109
+ Raises:
110
+ BadParameter: If value not in choices
111
+ """
112
+ if not case_sensitive and isinstance(value, str):
113
+ if value.lower() not in [str(c).lower() for c in choices]:
114
+ raise BadParameter(
115
+ f"Invalid choice for {param_name}: {value} "
116
+ f"(choose from {', '.join(str(c) for c in choices)})"
117
+ )
118
+ elif value not in choices:
119
+ raise BadParameter(
120
+ f"Invalid choice for {param_name}: {value} "
121
+ f"(choose from {', '.join(str(c) for c in choices)})"
122
+ )
123
+
124
+ def load_config_file(
125
+ path: Union[str, Path],
126
+ format: str = 'auto',
127
+ required: bool = True
128
+ ) -> Dict[str, Any]:
129
+ """
130
+ Load configuration from file.
131
+
132
+ Args:
133
+ path: Path to config file
134
+ format: File format (json, yaml, or auto)
135
+ required: Whether file is required
136
+
137
+ Returns:
138
+ Configuration dictionary
139
+
140
+ Raises:
141
+ UsageError: If required file not found or invalid format
142
+ """
143
+ path = Path(os.path.expanduser(path))
144
+
145
+ if not path.exists():
146
+ if required:
147
+ raise UsageError(f"Config file not found: {path}")
148
+ return {}
149
+
150
+ # Auto-detect format from extension
151
+ if format == 'auto':
152
+ format = path.suffix.lstrip('.').lower()
153
+ if format not in ('json', 'yaml', 'yml'):
154
+ raise UsageError(f"Unsupported config format: {format}")
155
+
156
+ try:
157
+ with open(path) as f:
158
+ if format == 'json':
159
+ return json.load(f)
160
+ elif format in ('yaml', 'yml'):
161
+ return yaml.safe_load(f)
162
+ else:
163
+ raise UsageError(f"Unsupported config format: {format}")
164
+ except Exception as e:
165
+ raise UsageError(f"Error loading config file: {str(e)}")
166
+
167
+ def parse_key_value(
168
+ value: str,
169
+ separator: str = '='
170
+ ) -> tuple:
171
+ """
172
+ Parse key-value string.
173
+
174
+ Args:
175
+ value: String in format "key=value"
176
+ separator: Key-value separator
177
+
178
+ Returns:
179
+ Tuple of (key, value)
180
+
181
+ Raises:
182
+ BadParameter: If string not in key=value format
183
+ """
184
+ try:
185
+ key, value = value.split(separator, 1)
186
+ return key.strip(), value.strip()
187
+ except ValueError:
188
+ raise BadParameter(
189
+ f"Invalid key-value pair: {value} (expected format: key{separator}value)"
190
+ )
191
+
192
+ def parse_list(
193
+ value: str,
194
+ separator: str = ','
195
+ ) -> List[str]:
196
+ """
197
+ Parse comma-separated list.
198
+
199
+ Args:
200
+ value: Comma-separated string
201
+ separator: List item separator
202
+
203
+ Returns:
204
+ List of strings
205
+ """
206
+ return [x.strip() for x in value.split(separator) if x.strip()]
207
+
208
+ def parse_dict(
209
+ value: str,
210
+ item_separator: str = ',',
211
+ key_value_separator: str = '='
212
+ ) -> Dict[str, str]:
213
+ """
214
+ Parse dictionary string.
215
+
216
+ Args:
217
+ value: String in format "key1=value1,key2=value2"
218
+ item_separator: Separator between items
219
+ key_value_separator: Separator between keys and values
220
+
221
+ Returns:
222
+ Dictionary of key-value pairs
223
+
224
+ Example:
225
+ >>> parse_dict("name=test,count=42")
226
+ {'name': 'test', 'count': '42'}
227
+ """
228
+ result = {}
229
+ if not value:
230
+ return result
231
+
232
+ items = parse_list(value, item_separator)
233
+ for item in items:
234
+ key, value = parse_key_value(item, key_value_separator)
235
+ result[key] = value
236
+
237
+ return result
238
+
239
+ def get_env_var(
240
+ name: str,
241
+ type_: Type = str,
242
+ required: bool = False,
243
+ default: Any = None
244
+ ) -> Any:
245
+ """
246
+ Get and validate environment variable.
247
+
248
+ Args:
249
+ name: Environment variable name
250
+ type_: Expected type
251
+ required: Whether variable is required
252
+ default: Default value if not set
253
+
254
+ Returns:
255
+ Environment variable value
256
+
257
+ Raises:
258
+ UsageError: If required variable not set
259
+ """
260
+ value = os.environ.get(name)
261
+
262
+ if value is None:
263
+ if required:
264
+ raise UsageError(f"Required environment variable not set: {name}")
265
+ return default
266
+
267
+ return convert_type(value, type_, name)
webscout/version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "8.2.7"
1
+ __version__ = "8.2.9"
2
2
  __prog__ = "webscout"