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.
- webscout/AIauto.py +34 -16
- webscout/AIbase.py +96 -37
- webscout/AIutel.py +491 -87
- webscout/Bard.py +441 -323
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Litlogger/README.md +10 -0
- webscout/Litlogger/__init__.py +7 -59
- webscout/Litlogger/formats.py +4 -0
- webscout/Litlogger/handlers.py +103 -0
- webscout/Litlogger/levels.py +13 -0
- webscout/Litlogger/logger.py +92 -0
- webscout/Provider/AISEARCH/Perplexity.py +332 -358
- webscout/Provider/AISEARCH/felo_search.py +9 -35
- webscout/Provider/AISEARCH/genspark_search.py +30 -56
- webscout/Provider/AISEARCH/hika_search.py +4 -16
- webscout/Provider/AISEARCH/iask_search.py +410 -436
- webscout/Provider/AISEARCH/monica_search.py +4 -30
- webscout/Provider/AISEARCH/scira_search.py +6 -32
- webscout/Provider/AISEARCH/webpilotai_search.py +38 -64
- webscout/Provider/Blackboxai.py +155 -35
- webscout/Provider/ChatSandbox.py +2 -1
- webscout/Provider/Deepinfra.py +339 -339
- webscout/Provider/ExaChat.py +358 -358
- webscout/Provider/Gemini.py +169 -169
- webscout/Provider/GithubChat.py +1 -2
- webscout/Provider/Glider.py +3 -3
- webscout/Provider/HeckAI.py +172 -82
- webscout/Provider/LambdaChat.py +1 -0
- webscout/Provider/MCPCore.py +7 -3
- webscout/Provider/OPENAI/BLACKBOXAI.py +421 -139
- webscout/Provider/OPENAI/Cloudflare.py +38 -21
- webscout/Provider/OPENAI/FalconH1.py +457 -0
- webscout/Provider/OPENAI/FreeGemini.py +35 -18
- webscout/Provider/OPENAI/NEMOTRON.py +34 -34
- webscout/Provider/OPENAI/PI.py +427 -0
- webscout/Provider/OPENAI/Qwen3.py +304 -0
- webscout/Provider/OPENAI/README.md +952 -1253
- webscout/Provider/OPENAI/TwoAI.py +374 -0
- webscout/Provider/OPENAI/__init__.py +7 -1
- webscout/Provider/OPENAI/ai4chat.py +73 -63
- webscout/Provider/OPENAI/api.py +869 -644
- webscout/Provider/OPENAI/base.py +2 -0
- webscout/Provider/OPENAI/c4ai.py +34 -13
- webscout/Provider/OPENAI/chatgpt.py +575 -556
- webscout/Provider/OPENAI/chatgptclone.py +512 -487
- webscout/Provider/OPENAI/chatsandbox.py +11 -6
- webscout/Provider/OPENAI/copilot.py +258 -0
- webscout/Provider/OPENAI/deepinfra.py +327 -318
- webscout/Provider/OPENAI/e2b.py +140 -104
- webscout/Provider/OPENAI/exaai.py +420 -411
- webscout/Provider/OPENAI/exachat.py +448 -443
- webscout/Provider/OPENAI/flowith.py +7 -3
- webscout/Provider/OPENAI/freeaichat.py +12 -8
- webscout/Provider/OPENAI/glider.py +15 -8
- webscout/Provider/OPENAI/groq.py +5 -2
- webscout/Provider/OPENAI/heckai.py +311 -307
- webscout/Provider/OPENAI/llmchatco.py +9 -7
- webscout/Provider/OPENAI/mcpcore.py +18 -9
- webscout/Provider/OPENAI/multichat.py +7 -5
- webscout/Provider/OPENAI/netwrck.py +16 -11
- webscout/Provider/OPENAI/oivscode.py +290 -0
- webscout/Provider/OPENAI/opkfc.py +507 -496
- webscout/Provider/OPENAI/pydantic_imports.py +172 -0
- webscout/Provider/OPENAI/scirachat.py +29 -17
- webscout/Provider/OPENAI/sonus.py +308 -303
- webscout/Provider/OPENAI/standardinput.py +442 -433
- webscout/Provider/OPENAI/textpollinations.py +18 -11
- webscout/Provider/OPENAI/toolbaz.py +419 -413
- webscout/Provider/OPENAI/typefully.py +17 -10
- webscout/Provider/OPENAI/typegpt.py +21 -11
- webscout/Provider/OPENAI/uncovrAI.py +477 -462
- webscout/Provider/OPENAI/utils.py +90 -79
- webscout/Provider/OPENAI/venice.py +435 -425
- webscout/Provider/OPENAI/wisecat.py +387 -381
- webscout/Provider/OPENAI/writecream.py +166 -163
- webscout/Provider/OPENAI/x0gpt.py +26 -37
- webscout/Provider/OPENAI/yep.py +384 -356
- webscout/Provider/PI.py +2 -1
- webscout/Provider/TTI/README.md +55 -101
- webscout/Provider/TTI/__init__.py +4 -9
- webscout/Provider/TTI/aiarta.py +365 -0
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/base.py +64 -0
- webscout/Provider/TTI/fastflux.py +200 -0
- webscout/Provider/TTI/magicstudio.py +201 -0
- webscout/Provider/TTI/piclumen.py +203 -0
- webscout/Provider/TTI/pixelmuse.py +225 -0
- webscout/Provider/TTI/pollinations.py +221 -0
- webscout/Provider/TTI/utils.py +11 -0
- webscout/Provider/TTS/__init__.py +2 -1
- webscout/Provider/TTS/base.py +159 -159
- webscout/Provider/TTS/openai_fm.py +129 -0
- webscout/Provider/TextPollinationsAI.py +308 -308
- webscout/Provider/TwoAI.py +239 -44
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/puterjs.py +635 -0
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Writecream.py +246 -246
- webscout/Provider/__init__.py +2 -2
- webscout/Provider/ai4chat.py +33 -8
- webscout/Provider/granite.py +41 -6
- webscout/Provider/koala.py +169 -169
- webscout/Provider/oivscode.py +309 -0
- webscout/Provider/samurai.py +3 -2
- webscout/Provider/scnet.py +1 -0
- webscout/Provider/typegpt.py +3 -3
- webscout/Provider/uncovr.py +368 -368
- webscout/client.py +70 -0
- webscout/litprinter/__init__.py +58 -58
- webscout/optimizers.py +419 -419
- webscout/scout/README.md +3 -1
- webscout/scout/core/crawler.py +134 -64
- webscout/scout/core/scout.py +148 -109
- webscout/scout/element.py +106 -88
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/plugins/manager.py +9 -2
- webscout/version.py +1 -1
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/METADATA +160 -35
- webscout-8.3.dist-info/RECORD +290 -0
- {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/WHEEL +1 -1
- {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/entry_points.txt +1 -0
- webscout/Litlogger/Readme.md +0 -175
- webscout/Litlogger/core/__init__.py +0 -6
- webscout/Litlogger/core/level.py +0 -23
- webscout/Litlogger/core/logger.py +0 -165
- webscout/Litlogger/handlers/__init__.py +0 -12
- webscout/Litlogger/handlers/console.py +0 -33
- webscout/Litlogger/handlers/file.py +0 -143
- webscout/Litlogger/handlers/network.py +0 -173
- webscout/Litlogger/styles/__init__.py +0 -7
- webscout/Litlogger/styles/colors.py +0 -249
- webscout/Litlogger/styles/formats.py +0 -458
- webscout/Litlogger/styles/text.py +0 -87
- webscout/Litlogger/utils/__init__.py +0 -6
- webscout/Litlogger/utils/detectors.py +0 -153
- webscout/Litlogger/utils/formatters.py +0 -200
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/TTI/AiForce/README.md +0 -159
- webscout/Provider/TTI/AiForce/__init__.py +0 -22
- webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
- webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
- webscout/Provider/TTI/FreeAIPlayground/README.md +0 -99
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
- webscout/Provider/TTI/ImgSys/README.md +0 -174
- webscout/Provider/TTI/ImgSys/__init__.py +0 -23
- webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
- webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
- webscout/Provider/TTI/MagicStudio/README.md +0 -101
- webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
- webscout/Provider/TTI/Nexra/README.md +0 -155
- webscout/Provider/TTI/Nexra/__init__.py +0 -22
- webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
- webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
- webscout/Provider/TTI/PollinationsAI/README.md +0 -146
- webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
- webscout/Provider/TTI/aiarta/README.md +0 -134
- webscout/Provider/TTI/aiarta/__init__.py +0 -2
- webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
- webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
- webscout/Provider/TTI/artbit/README.md +0 -100
- webscout/Provider/TTI/artbit/__init__.py +0 -22
- webscout/Provider/TTI/artbit/async_artbit.py +0 -155
- webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
- webscout/Provider/TTI/fastflux/README.md +0 -129
- webscout/Provider/TTI/fastflux/__init__.py +0 -22
- webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
- webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
- webscout/Provider/TTI/huggingface/README.md +0 -114
- webscout/Provider/TTI/huggingface/__init__.py +0 -22
- webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
- webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
- webscout/Provider/TTI/piclumen/README.md +0 -161
- webscout/Provider/TTI/piclumen/__init__.py +0 -23
- webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
- webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
- webscout/Provider/TTI/pixelmuse/README.md +0 -79
- webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
- webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
- webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
- webscout/Provider/TTI/talkai/README.md +0 -139
- webscout/Provider/TTI/talkai/__init__.py +0 -4
- webscout/Provider/TTI/talkai/async_talkai.py +0 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
- webscout/Provider/UNFINISHED/oivscode.py +0 -351
- webscout-8.2.8.dist-info/RECORD +0 -334
- {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.8.dist-info → webscout-8.3.dist-info}/top_level.txt +0 -0
webscout/Litlogger/Readme.md
DELETED
|
@@ -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
|
webscout/Litlogger/core/level.py
DELETED
|
@@ -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
|