webscout 8.2.8__py3-none-any.whl → 8.3__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 (197) hide show
  1. webscout/AIauto.py +34 -16
  2. webscout/AIbase.py +96 -37
  3. webscout/AIutel.py +491 -87
  4. webscout/Bard.py +441 -323
  5. webscout/Extra/GitToolkit/__init__.py +10 -10
  6. webscout/Extra/YTToolkit/ytapi/video.py +232 -232
  7. webscout/Litlogger/README.md +10 -0
  8. webscout/Litlogger/__init__.py +7 -59
  9. webscout/Litlogger/formats.py +4 -0
  10. webscout/Litlogger/handlers.py +103 -0
  11. webscout/Litlogger/levels.py +13 -0
  12. webscout/Litlogger/logger.py +92 -0
  13. webscout/Provider/AISEARCH/Perplexity.py +332 -358
  14. webscout/Provider/AISEARCH/felo_search.py +9 -35
  15. webscout/Provider/AISEARCH/genspark_search.py +30 -56
  16. webscout/Provider/AISEARCH/hika_search.py +4 -16
  17. webscout/Provider/AISEARCH/iask_search.py +410 -436
  18. webscout/Provider/AISEARCH/monica_search.py +4 -30
  19. webscout/Provider/AISEARCH/scira_search.py +6 -32
  20. webscout/Provider/AISEARCH/webpilotai_search.py +38 -64
  21. webscout/Provider/Blackboxai.py +155 -35
  22. webscout/Provider/ChatSandbox.py +2 -1
  23. webscout/Provider/Deepinfra.py +339 -339
  24. webscout/Provider/ExaChat.py +358 -358
  25. webscout/Provider/Gemini.py +169 -169
  26. webscout/Provider/GithubChat.py +1 -2
  27. webscout/Provider/Glider.py +3 -3
  28. webscout/Provider/HeckAI.py +172 -82
  29. webscout/Provider/LambdaChat.py +1 -0
  30. webscout/Provider/MCPCore.py +7 -3
  31. webscout/Provider/OPENAI/BLACKBOXAI.py +421 -139
  32. webscout/Provider/OPENAI/Cloudflare.py +38 -21
  33. webscout/Provider/OPENAI/FalconH1.py +457 -0
  34. webscout/Provider/OPENAI/FreeGemini.py +35 -18
  35. webscout/Provider/OPENAI/NEMOTRON.py +34 -34
  36. webscout/Provider/OPENAI/PI.py +427 -0
  37. webscout/Provider/OPENAI/Qwen3.py +304 -0
  38. webscout/Provider/OPENAI/README.md +952 -1253
  39. webscout/Provider/OPENAI/TwoAI.py +374 -0
  40. webscout/Provider/OPENAI/__init__.py +7 -1
  41. webscout/Provider/OPENAI/ai4chat.py +73 -63
  42. webscout/Provider/OPENAI/api.py +869 -644
  43. webscout/Provider/OPENAI/base.py +2 -0
  44. webscout/Provider/OPENAI/c4ai.py +34 -13
  45. webscout/Provider/OPENAI/chatgpt.py +575 -556
  46. webscout/Provider/OPENAI/chatgptclone.py +512 -487
  47. webscout/Provider/OPENAI/chatsandbox.py +11 -6
  48. webscout/Provider/OPENAI/copilot.py +258 -0
  49. webscout/Provider/OPENAI/deepinfra.py +327 -318
  50. webscout/Provider/OPENAI/e2b.py +140 -104
  51. webscout/Provider/OPENAI/exaai.py +420 -411
  52. webscout/Provider/OPENAI/exachat.py +448 -443
  53. webscout/Provider/OPENAI/flowith.py +7 -3
  54. webscout/Provider/OPENAI/freeaichat.py +12 -8
  55. webscout/Provider/OPENAI/glider.py +15 -8
  56. webscout/Provider/OPENAI/groq.py +5 -2
  57. webscout/Provider/OPENAI/heckai.py +311 -307
  58. webscout/Provider/OPENAI/llmchatco.py +9 -7
  59. webscout/Provider/OPENAI/mcpcore.py +18 -9
  60. webscout/Provider/OPENAI/multichat.py +7 -5
  61. webscout/Provider/OPENAI/netwrck.py +16 -11
  62. webscout/Provider/OPENAI/oivscode.py +290 -0
  63. webscout/Provider/OPENAI/opkfc.py +507 -496
  64. webscout/Provider/OPENAI/pydantic_imports.py +172 -0
  65. webscout/Provider/OPENAI/scirachat.py +29 -17
  66. webscout/Provider/OPENAI/sonus.py +308 -303
  67. webscout/Provider/OPENAI/standardinput.py +442 -433
  68. webscout/Provider/OPENAI/textpollinations.py +18 -11
  69. webscout/Provider/OPENAI/toolbaz.py +419 -413
  70. webscout/Provider/OPENAI/typefully.py +17 -10
  71. webscout/Provider/OPENAI/typegpt.py +21 -11
  72. webscout/Provider/OPENAI/uncovrAI.py +477 -462
  73. webscout/Provider/OPENAI/utils.py +90 -79
  74. webscout/Provider/OPENAI/venice.py +435 -425
  75. webscout/Provider/OPENAI/wisecat.py +387 -381
  76. webscout/Provider/OPENAI/writecream.py +166 -163
  77. webscout/Provider/OPENAI/x0gpt.py +26 -37
  78. webscout/Provider/OPENAI/yep.py +384 -356
  79. webscout/Provider/PI.py +2 -1
  80. webscout/Provider/TTI/README.md +55 -101
  81. webscout/Provider/TTI/__init__.py +4 -9
  82. webscout/Provider/TTI/aiarta.py +365 -0
  83. webscout/Provider/TTI/artbit.py +0 -0
  84. webscout/Provider/TTI/base.py +64 -0
  85. webscout/Provider/TTI/fastflux.py +200 -0
  86. webscout/Provider/TTI/magicstudio.py +201 -0
  87. webscout/Provider/TTI/piclumen.py +203 -0
  88. webscout/Provider/TTI/pixelmuse.py +225 -0
  89. webscout/Provider/TTI/pollinations.py +221 -0
  90. webscout/Provider/TTI/utils.py +11 -0
  91. webscout/Provider/TTS/__init__.py +2 -1
  92. webscout/Provider/TTS/base.py +159 -159
  93. webscout/Provider/TTS/openai_fm.py +129 -0
  94. webscout/Provider/TextPollinationsAI.py +308 -308
  95. webscout/Provider/TwoAI.py +239 -44
  96. webscout/Provider/UNFINISHED/Youchat.py +330 -330
  97. webscout/Provider/UNFINISHED/puterjs.py +635 -0
  98. webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
  99. webscout/Provider/Writecream.py +246 -246
  100. webscout/Provider/__init__.py +2 -2
  101. webscout/Provider/ai4chat.py +33 -8
  102. webscout/Provider/granite.py +41 -6
  103. webscout/Provider/koala.py +169 -169
  104. webscout/Provider/oivscode.py +309 -0
  105. webscout/Provider/samurai.py +3 -2
  106. webscout/Provider/scnet.py +1 -0
  107. webscout/Provider/typegpt.py +3 -3
  108. webscout/Provider/uncovr.py +368 -368
  109. webscout/client.py +70 -0
  110. webscout/litprinter/__init__.py +58 -58
  111. webscout/optimizers.py +419 -419
  112. webscout/scout/README.md +3 -1
  113. webscout/scout/core/crawler.py +134 -64
  114. webscout/scout/core/scout.py +148 -109
  115. webscout/scout/element.py +106 -88
  116. webscout/swiftcli/Readme.md +323 -323
  117. webscout/swiftcli/plugins/manager.py +9 -2
  118. webscout/version.py +1 -1
  119. webscout/zeroart/__init__.py +134 -134
  120. webscout/zeroart/effects.py +100 -100
  121. webscout/zeroart/fonts.py +1238 -1238
  122. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/METADATA +160 -35
  123. webscout-8.3.dist-info/RECORD +290 -0
  124. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/WHEEL +1 -1
  125. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/entry_points.txt +1 -0
  126. webscout/Litlogger/Readme.md +0 -175
  127. webscout/Litlogger/core/__init__.py +0 -6
  128. webscout/Litlogger/core/level.py +0 -23
  129. webscout/Litlogger/core/logger.py +0 -165
  130. webscout/Litlogger/handlers/__init__.py +0 -12
  131. webscout/Litlogger/handlers/console.py +0 -33
  132. webscout/Litlogger/handlers/file.py +0 -143
  133. webscout/Litlogger/handlers/network.py +0 -173
  134. webscout/Litlogger/styles/__init__.py +0 -7
  135. webscout/Litlogger/styles/colors.py +0 -249
  136. webscout/Litlogger/styles/formats.py +0 -458
  137. webscout/Litlogger/styles/text.py +0 -87
  138. webscout/Litlogger/utils/__init__.py +0 -6
  139. webscout/Litlogger/utils/detectors.py +0 -153
  140. webscout/Litlogger/utils/formatters.py +0 -200
  141. webscout/Provider/ChatGPTGratis.py +0 -194
  142. webscout/Provider/TTI/AiForce/README.md +0 -159
  143. webscout/Provider/TTI/AiForce/__init__.py +0 -22
  144. webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
  145. webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
  146. webscout/Provider/TTI/FreeAIPlayground/README.md +0 -99
  147. webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
  148. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
  149. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
  150. webscout/Provider/TTI/ImgSys/README.md +0 -174
  151. webscout/Provider/TTI/ImgSys/__init__.py +0 -23
  152. webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
  153. webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
  154. webscout/Provider/TTI/MagicStudio/README.md +0 -101
  155. webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
  156. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
  157. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
  158. webscout/Provider/TTI/Nexra/README.md +0 -155
  159. webscout/Provider/TTI/Nexra/__init__.py +0 -22
  160. webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
  161. webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
  162. webscout/Provider/TTI/PollinationsAI/README.md +0 -146
  163. webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
  164. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
  165. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
  166. webscout/Provider/TTI/aiarta/README.md +0 -134
  167. webscout/Provider/TTI/aiarta/__init__.py +0 -2
  168. webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
  169. webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
  170. webscout/Provider/TTI/artbit/README.md +0 -100
  171. webscout/Provider/TTI/artbit/__init__.py +0 -22
  172. webscout/Provider/TTI/artbit/async_artbit.py +0 -155
  173. webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
  174. webscout/Provider/TTI/fastflux/README.md +0 -129
  175. webscout/Provider/TTI/fastflux/__init__.py +0 -22
  176. webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
  177. webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
  178. webscout/Provider/TTI/huggingface/README.md +0 -114
  179. webscout/Provider/TTI/huggingface/__init__.py +0 -22
  180. webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
  181. webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
  182. webscout/Provider/TTI/piclumen/README.md +0 -161
  183. webscout/Provider/TTI/piclumen/__init__.py +0 -23
  184. webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
  185. webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
  186. webscout/Provider/TTI/pixelmuse/README.md +0 -79
  187. webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
  188. webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
  189. webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
  190. webscout/Provider/TTI/talkai/README.md +0 -139
  191. webscout/Provider/TTI/talkai/__init__.py +0 -4
  192. webscout/Provider/TTI/talkai/async_talkai.py +0 -229
  193. webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
  194. webscout/Provider/UNFINISHED/oivscode.py +0 -351
  195. webscout-8.2.8.dist-info/RECORD +0 -334
  196. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/licenses/LICENSE.md +0 -0
  197. {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/top_level.txt +0 -0
@@ -1,175 +0,0 @@
1
- # 📝 LitLogger - The Most Fire Logger You'll Ever Use!
2
-
3
- Yo fam! Meet LitLogger - your new logging bestie that's built different! 🔥 With smart level detection, fire color schemes, and emoji support, your logs never looked this good!
4
-
5
- ## 🚀 Quick Start
6
-
7
- ```python
8
- from webscout import LitLogger, LogFormat, ColorScheme
9
-
10
- # Create your logger with style
11
- logger = LitLogger(
12
- name="MyApp",
13
- format=LogFormat.MODERN_EMOJI,
14
- color_scheme=ColorScheme.CYBERPUNK
15
- )
16
-
17
- # Start logging with swag
18
- logger.info("App started! 🚀")
19
- logger.success("Mission accomplished! 💯")
20
- logger.warning("CPU getting spicy! 🌶️")
21
- logger.error("Houston, we got a problem! 🔧")
22
- ```
23
-
24
- ## 💫 Features That Hit Different
25
-
26
- ### 🎨 Fire Color Schemes
27
-
28
- ```python
29
- # Cyberpunk vibes
30
- logger = LitLogger(color_scheme=ColorScheme.CYBERPUNK)
31
-
32
- # Ocean feels
33
- logger = LitLogger(color_scheme=ColorScheme.OCEAN)
34
-
35
- # Matrix mode
36
- logger = LitLogger(color_scheme=ColorScheme.MATRIX)
37
-
38
- # Aurora lights
39
- logger = LitLogger(color_scheme=ColorScheme.AURORA)
40
-
41
- # Sunset mood
42
- logger = LitLogger(color_scheme=ColorScheme.SUNSET)
43
- ```
44
-
45
- ### 📝 Lit Log Formats
46
-
47
- ```python
48
- # Modern with emojis
49
- logger = LitLogger(format=LogFormat.MODERN_EMOJI)
50
- # Output: 🚀 [2024-01-20 15:30:45] INFO Server started!
51
-
52
- # Clean and minimal
53
- logger = LitLogger(format=LogFormat.MODERN_CLEAN)
54
- # Output: 2024-01-20 15:30:45 INFO Server started
55
-
56
- # Boxed style
57
- logger = LitLogger(format=LogFormat.BOXED)
58
- # Output: ╭─────────────────────╮
59
- # │ [2024-01-20 15:30:45]
60
- # │ INFO - MyApp
61
- # │ Server started!
62
- # ╰─────────────────────╯
63
-
64
- # Japanese style
65
- logger = LitLogger(format=LogFormat.MODERN_BRACKET)
66
- # Output: 【2024-01-20 15:30:45】「INFO」Server started
67
- ```
68
-
69
- ### 🧠 Smart Level Detection
70
-
71
- ```python
72
- # Auto-detects appropriate log level
73
- logger.auto("Starting server...") # INFO
74
- logger.auto("CPU usage at 95%") # WARNING
75
- logger.auto("404: Not Found") # ERROR
76
- logger.auto("x = calculate(y)") # DEBUG
77
-
78
- # With context
79
- logger.auto("Memory usage: 90%", memory=90) # WARNING
80
- logger.auto("Response time: 1500ms", latency=1500) # WARNING
81
- ```
82
-
83
- ### 🎯 Log Levels
84
-
85
- ```python
86
- # All the levels you need
87
- logger.trace("Entering function...")
88
- logger.debug("x = 42")
89
- logger.info("Server started")
90
- logger.success("Task completed")
91
- logger.warning("Running low on memory")
92
- logger.error("Failed to connect")
93
- logger.critical("System crash!")
94
- ```
95
-
96
- ## 🌟 Real-World Examples
97
-
98
- ### API Server Logging
99
-
100
- ```python
101
- logger = LitLogger(name="APIServer", format=LogFormat.MODERN_EMOJI)
102
-
103
- def handle_request():
104
- logger.info("Received new request 📥")
105
- try:
106
- # Process request
107
- logger.success("Request processed successfully ✨")
108
- except Exception as e:
109
- logger.error(f"Request failed: {e} 💀")
110
- ```
111
-
112
- ### Performance Monitoring
113
-
114
- ```python
115
- logger = LitLogger(name="Monitor", color_scheme=ColorScheme.MATRIX)
116
-
117
- def monitor_system():
118
- metrics = get_system_metrics()
119
- logger.auto(
120
- f"CPU: {metrics['cpu']}%, Memory: {metrics['memory']}%",
121
- cpu=metrics['cpu'],
122
- memory=metrics['memory']
123
- )
124
- ```
125
-
126
- ### Development Debugging
127
-
128
- ```python
129
- logger = LitLogger(name="Debug", format=LogFormat.DETAILED)
130
-
131
- def complex_calculation(x, y):
132
- logger.debug(f"Input: x={x}, y={y}")
133
- result = x * y
134
- logger.debug(f"Result: {result}")
135
- return result
136
- ```
137
-
138
- ## 🎮 Pro Tips
139
-
140
- 1. **Custom Color Schemes**: Create your own vibe
141
- ```python
142
- my_scheme = {
143
- "trace": (128, 128, 255), # Your colors
144
- "debug": (255, 0, 255),
145
- "info": (0, 255, 255)
146
- }
147
- logger = LitLogger(color_scheme=my_scheme)
148
- ```
149
-
150
- 2. **Log to File**: Keep records with style
151
- ```python
152
- logger = LitLogger(
153
- name="MyApp",
154
- log_path="logs/app.log",
155
- console_output=True # Both file and console
156
- )
157
- ```
158
-
159
- 3. **Smart Context**: Let the logger decide
160
- ```python
161
- # Automatically chooses log level based on content
162
- logger.auto("Database connection failed") # ERROR
163
- logger.auto("Cache hit ratio: 95%") # INFO
164
- ```
165
-
166
- ## 🔥 Why LitLogger?
167
-
168
- - 🎨 Beautiful, colorful output
169
- - 🧠 Smart level detection
170
- - 📱 Multiple output formats
171
- - 🌈 Customizable color schemes
172
- - 💪 Easy to use, hard to mess up
173
- - ⚡ Fast and lightweight
174
-
175
- Made with 💖 by the HelpingAI team
@@ -1,6 +0,0 @@
1
- """Core logging functionality."""
2
-
3
- from .logger import Logger
4
- from .level import LogLevel
5
-
6
- __all__ = ["Logger", "LogLevel"]
@@ -1,23 +0,0 @@
1
- from enum import Enum
2
-
3
- class LogLevel(Enum):
4
- NOTSET = 0
5
- DEBUG = 10
6
- INFO = 20
7
- WARNING = 30
8
- ERROR = 40
9
- CRITICAL = 50
10
-
11
- @staticmethod
12
- def get_level(level_str: str) -> 'LogLevel':
13
- if not level_str:
14
- return LogLevel.NOTSET
15
- try:
16
- return LogLevel[level_str.upper()]
17
- except KeyError:
18
- raise ValueError(f"Invalid log level: {level_str}")
19
-
20
- def __lt__(self, other):
21
- if isinstance(other, LogLevel):
22
- return self.value < other.value
23
- return NotImplemented
@@ -1,165 +0,0 @@
1
-
2
- import asyncio
3
- import sys
4
- import threading
5
- import traceback
6
- from datetime import datetime
7
- from typing import List, Union
8
-
9
- from ..core.level import LogLevel
10
- from ..styles.formats import LogFormat
11
-
12
- class Logger:
13
- # Emoji mappings for different log levels
14
- LEVEL_EMOJIS = {
15
- LogLevel.DEBUG: "🔍",
16
- LogLevel.INFO: "ℹ️",
17
- LogLevel.WARNING: "⚠️",
18
- LogLevel.ERROR: "❌",
19
- LogLevel.CRITICAL: "🔥"
20
- }
21
-
22
- def __init__(
23
- self,
24
- name: str = "LitLogger",
25
- level: Union[str, LogLevel, None] = None,
26
- format: Union[str, LogFormat] = LogFormat.MODERN_EMOJI,
27
- handlers: List = None,
28
- enable_colors: bool = True,
29
- async_mode: bool = False,
30
- show_thread: bool = True,
31
- show_context: bool = True
32
- ):
33
- self.name = name
34
- self.level = LogLevel.NOTSET if level is None else (
35
- LogLevel.get_level(level) if isinstance(level, str) else level
36
- )
37
- self.format = format
38
- self.enable_colors = enable_colors
39
- self.async_mode = async_mode
40
- self.show_thread = show_thread
41
- self.show_context = show_context
42
- self._context_data = {}
43
-
44
- # Initialize with default console handler if none provided
45
- if handlers is None:
46
- from ..handlers.console import ConsoleHandler
47
- self.handlers = [ConsoleHandler(level=self.level)]
48
- else:
49
- self.handlers = handlers
50
-
51
- def _format_message(self, level: LogLevel, message: str, **kwargs) -> str:
52
- now = datetime.now()
53
- emoji = self.LEVEL_EMOJIS.get(level, "") if self.enable_colors else ""
54
-
55
- log_data = {
56
- "timestamp": now.strftime("%H:%M:%S"),
57
- "level": level.name,
58
- "name": self.name,
59
- "message": str(message),
60
- "emoji": emoji,
61
- "thread": threading.current_thread().name if self.show_thread else "",
62
- }
63
-
64
- # Add context data
65
- if self.show_context and self._context_data:
66
- log_data.update(self._context_data)
67
-
68
- # Add extra kwargs
69
- log_data.update(kwargs)
70
-
71
- # Format exception if present
72
- if 'exc_info' in kwargs:
73
- exc_info = kwargs['exc_info']
74
- if exc_info:
75
- exception_text = ''.join(traceback.format_exception(*exc_info))
76
- log_data['message'] = f"{message}\n{exception_text}"
77
-
78
- try:
79
- base_message = f"{emoji} [{log_data['timestamp']}] {level.name} {log_data['message']}"
80
- return base_message
81
- except Exception as e:
82
- return f"[{log_data['timestamp']}] {level.name}: {message}"
83
-
84
- def _log(self, level: LogLevel, message: str, **kwargs):
85
- if self.async_mode:
86
- loop = asyncio.get_event_loop()
87
- if loop.is_running():
88
- return asyncio.create_task(self._async_log(level, message, **kwargs))
89
- else:
90
- return loop.run_until_complete(self._async_log(level, message, **kwargs))
91
- return self._sync_log(level, message, **kwargs)
92
-
93
- def debug(self, message: str, **kwargs):
94
- self._log(LogLevel.DEBUG, message, **kwargs)
95
-
96
- def info(self, message: str, **kwargs):
97
- self._log(LogLevel.INFO, message, **kwargs)
98
-
99
- def warning(self, message: str, **kwargs):
100
- self._log(LogLevel.WARNING, message, **kwargs)
101
-
102
- def error(self, message: str, **kwargs):
103
- self._log(LogLevel.ERROR, message, **kwargs)
104
-
105
- def critical(self, message: str, **kwargs):
106
- self._log(LogLevel.CRITICAL, message, **kwargs)
107
-
108
- def exception(self, message: str, exc_info=True, **kwargs):
109
- """
110
- Log an exception with traceback.
111
-
112
- Args:
113
- message: The message to log
114
- exc_info: If True, adds exception info to the message. Can also be a tuple (type, value, traceback)
115
- **kwargs: Additional key-value pairs to log
116
- """
117
- if exc_info:
118
- if not isinstance(exc_info, tuple):
119
- exc_info = sys.exc_info()
120
- kwargs['exc_info'] = exc_info
121
- self.error(message, **kwargs)
122
-
123
- def _sync_log(self, level: LogLevel, message: str, **kwargs):
124
- if self._should_log(level):
125
- formatted_message = self._format_message(level, message, **kwargs)
126
- for handler in self.handlers:
127
- if handler.level == LogLevel.NOTSET or level.value >= handler.level.value:
128
- handler.emit(formatted_message, level)
129
-
130
- async def _async_log(self, level: LogLevel, message: str, **kwargs):
131
- if self._should_log(level):
132
- formatted_message = self._format_message(level, message, **kwargs)
133
- tasks = []
134
- for handler in self.handlers:
135
- if handler.level == LogLevel.NOTSET or level.value >= handler.level.value:
136
- if hasattr(handler, 'async_emit'):
137
- tasks.append(handler.async_emit(formatted_message, level))
138
- else:
139
- tasks.append(asyncio.to_thread(handler.emit, formatted_message, level))
140
- await asyncio.gather(*tasks)
141
-
142
- def _should_log(self, level: LogLevel) -> bool:
143
- return self.level == LogLevel.NOTSET or level.value >= self.level.value
144
-
145
- def set_context(self, **kwargs):
146
- self._context_data.update(kwargs)
147
-
148
- def clear_context(self):
149
- self._context_data.clear()
150
-
151
- def set_style(self, style: str):
152
- """Set logger style format."""
153
- if style in LogFormat.TEMPLATES:
154
- self.format = LogFormat.TEMPLATES[style]
155
- else:
156
- raise ValueError(f"Unknown style: {style}")
157
-
158
- def __enter__(self):
159
- return self
160
-
161
- def __exit__(self, exc_type, exc_val, exc_tb):
162
- if exc_type is not None:
163
- self.error(f"Context exited with error: {exc_val}")
164
- return False
165
- return True
@@ -1,12 +0,0 @@
1
- """Log output handlers for different destinations."""
2
-
3
- from .console import ConsoleHandler, ErrorConsoleHandler
4
- from .file import FileHandler
5
- from .network import NetworkHandler
6
-
7
- __all__ = [
8
- "ConsoleHandler",
9
- "ErrorConsoleHandler",
10
- "FileHandler",
11
- "NetworkHandler"
12
- ]
@@ -1,33 +0,0 @@
1
- import sys
2
- from ..core.level import LogLevel
3
- from ..styles.colors import LogColors
4
-
5
- class ConsoleHandler:
6
- def __init__(self, level: LogLevel = LogLevel.DEBUG, stream=sys.stdout):
7
- self.level = level
8
- self.stream = stream
9
- self.colors = LogColors()
10
-
11
- def emit(self, message: str, level: LogLevel):
12
- """Write the colored message to the console."""
13
- try:
14
- # Apply color based on log level
15
- color = LogColors.LEVEL_COLORS.get(level, LogColors.RESET)
16
- colored_message = f"{color}{message}{LogColors.RESET}"
17
- print(colored_message, file=self.stream, flush=True)
18
- except Exception as e:
19
- # Fallback to plain printing if coloring fails
20
- print(message, file=self.stream, flush=True)
21
-
22
- async def async_emit(self, message: str, level: LogLevel):
23
- """
24
- Asynchronously write log message to console.
25
- Just calls emit() since console output is generally fast enough.
26
- """
27
- self.emit(message, level)
28
-
29
- class ErrorConsoleHandler(ConsoleHandler):
30
- """Specialized handler that writes to stderr."""
31
-
32
- def __init__(self, level: LogLevel = LogLevel.ERROR):
33
- super().__init__(stream=sys.stderr, level=level)
@@ -1,143 +0,0 @@
1
- import os
2
- import time
3
- from datetime import datetime
4
- from pathlib import Path
5
- from typing import Union
6
- from ..core.level import LogLevel
7
-
8
- class FileHandler:
9
- """Handler for outputting log messages to a file with optional rotation."""
10
-
11
- def __init__(
12
- self,
13
- filename: Union[str, Path],
14
- mode: str = "a",
15
- encoding: str = "utf-8",
16
- level: LogLevel = LogLevel.DEBUG,
17
- max_bytes: int = 0,
18
- backup_count: int = 0,
19
- rotate_on_time: bool = False,
20
- time_interval: str = "D" # D=daily, H=hourly, M=monthly
21
- ):
22
- """
23
- Initialize file handler with rotation options.
24
-
25
- Args:
26
- filename: Log file path
27
- mode: File open mode ('a' for append, 'w' for write)
28
- encoding: File encoding
29
- level: Minimum log level to output
30
- max_bytes: Max file size before rotation (0 = no size limit)
31
- backup_count: Number of backup files to keep (0 = no backups)
32
- rotate_on_time: Enable time-based rotation
33
- time_interval: Rotation interval ('D'=daily, 'H'=hourly, 'M'=monthly)
34
- """
35
- self.filename = Path(filename)
36
- self.mode = mode
37
- self.encoding = encoding
38
- self.level = level
39
- self.max_bytes = max_bytes
40
- self.backup_count = backup_count
41
- self.rotate_on_time = rotate_on_time
42
- self.time_interval = time_interval.upper()
43
-
44
- if self.time_interval not in ["D", "H", "M"]:
45
- raise ValueError("time_interval must be 'D', 'H', or 'M'")
46
-
47
- self._file = None
48
- self._current_size = 0
49
- self._last_rollover_time = time.time()
50
-
51
- # Create directory if it doesn't exist
52
- self.filename.parent.mkdir(parents=True, exist_ok=True)
53
-
54
- # Open the file
55
- self._open()
56
-
57
- def _open(self):
58
- """Open or reopen the log file."""
59
- if self._file:
60
- self._file.close()
61
-
62
- self._file = open(
63
- self.filename,
64
- mode=self.mode,
65
- encoding=self.encoding
66
- )
67
-
68
- self._current_size = self._file.tell()
69
- if self.mode == "a":
70
- self._current_size = self.filename.stat().st_size
71
-
72
- def _should_rollover(self) -> bool:
73
- """Check if file should be rolled over based on size or time."""
74
- if self.max_bytes > 0 and self._current_size >= self.max_bytes:
75
- return True
76
-
77
- if self.rotate_on_time:
78
- current_time = time.time()
79
- if self.time_interval == "H":
80
- interval = 3600 # 1 hour
81
- elif self.time_interval == "D":
82
- interval = 86400 # 1 day
83
- else: # Monthly
84
- now = datetime.now()
85
- if now.month == datetime.fromtimestamp(self._last_rollover_time).month:
86
- return False
87
- return True
88
-
89
- if current_time - self._last_rollover_time >= interval:
90
- return True
91
-
92
- return False
93
-
94
- def _do_rollover(self):
95
- """Perform log file rotation."""
96
- if self._file:
97
- self._file.close()
98
- self._file = None
99
-
100
- if self.backup_count > 0:
101
- # Shift existing backup files
102
- for i in range(self.backup_count - 1, 0, -1):
103
- sfn = f"{self.filename}.{i}"
104
- dfn = f"{self.filename}.{i + 1}"
105
- if os.path.exists(sfn):
106
- if os.path.exists(dfn):
107
- os.remove(dfn)
108
- os.rename(sfn, dfn)
109
-
110
- dfn = f"{self.filename}.1"
111
- if os.path.exists(dfn):
112
- os.remove(dfn)
113
- os.rename(self.filename, dfn)
114
-
115
- self._open()
116
- self._last_rollover_time = time.time()
117
-
118
- def emit(self, message: str, level: LogLevel):
119
- """Write log message to file if level is sufficient."""
120
- if level.value >= self.level.value:
121
- try:
122
- if self._should_rollover():
123
- self._do_rollover()
124
-
125
- self._file.write(message + "\n")
126
- self._file.flush()
127
- self._current_size = self._file.tell()
128
-
129
- except Exception as e:
130
- # Fallback to console on error
131
- import sys
132
- sys.stderr.write(f"Error in FileHandler: {e}\n")
133
- sys.stderr.write(message + "\n")
134
-
135
- async def async_emit(self, message: str, level: LogLevel):
136
- """Asynchronously write log message to file."""
137
- self.emit(message, level)
138
-
139
- def close(self):
140
- """Close the log file."""
141
- if self._file:
142
- self._file.close()
143
- self._file = None