webscout 8.2.6__py3-none-any.whl → 8.2.7__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/AIutel.py +97 -87
- webscout/version.py +1 -1
- {webscout-8.2.6.dist-info → webscout-8.2.7.dist-info}/METADATA +2 -15
- webscout-8.2.7.dist-info/RECORD +26 -0
- {webscout-8.2.6.dist-info → webscout-8.2.7.dist-info}/WHEEL +1 -1
- webscout-8.2.7.dist-info/entry_points.txt +3 -0
- webscout-8.2.7.dist-info/top_level.txt +1 -0
- webscout/Extra/GitToolkit/__init__.py +0 -10
- webscout/Extra/GitToolkit/gitapi/__init__.py +0 -12
- webscout/Extra/GitToolkit/gitapi/repository.py +0 -195
- webscout/Extra/GitToolkit/gitapi/user.py +0 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +0 -62
- webscout/Extra/YTToolkit/YTdownloader.py +0 -957
- webscout/Extra/YTToolkit/__init__.py +0 -3
- webscout/Extra/YTToolkit/transcriber.py +0 -476
- webscout/Extra/YTToolkit/ytapi/__init__.py +0 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +0 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +0 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +0 -45
- webscout/Extra/YTToolkit/ytapi/https.py +0 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +0 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +0 -59
- webscout/Extra/YTToolkit/ytapi/pool.py +0 -8
- webscout/Extra/YTToolkit/ytapi/query.py +0 -40
- webscout/Extra/YTToolkit/ytapi/stream.py +0 -63
- webscout/Extra/YTToolkit/ytapi/utils.py +0 -62
- webscout/Extra/YTToolkit/ytapi/video.py +0 -232
- webscout/Extra/__init__.py +0 -7
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -910
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/Extra/gguf.py +0 -684
- webscout/Extra/tempmail/__init__.py +0 -28
- webscout/Extra/tempmail/async_utils.py +0 -141
- webscout/Extra/tempmail/base.py +0 -161
- webscout/Extra/tempmail/cli.py +0 -187
- webscout/Extra/tempmail/emailnator.py +0 -84
- webscout/Extra/tempmail/mail_tm.py +0 -361
- webscout/Extra/tempmail/temp_mail_io.py +0 -292
- webscout/Extra/weather.py +0 -194
- webscout/Extra/weather_ascii.py +0 -76
- webscout/Litlogger/__init__.py +0 -67
- 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/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -250
- webscout/Provider/AISEARCH/ISou.py +0 -256
- webscout/Provider/AISEARCH/Perplexity.py +0 -359
- webscout/Provider/AISEARCH/__init__.py +0 -10
- webscout/Provider/AISEARCH/felo_search.py +0 -228
- webscout/Provider/AISEARCH/genspark_search.py +0 -208
- webscout/Provider/AISEARCH/hika_search.py +0 -198
- webscout/Provider/AISEARCH/iask_search.py +0 -436
- webscout/Provider/AISEARCH/monica_search.py +0 -246
- webscout/Provider/AISEARCH/scira_search.py +0 -322
- webscout/Provider/AISEARCH/webpilotai_search.py +0 -281
- webscout/Provider/Aitopia.py +0 -316
- webscout/Provider/AllenAI.py +0 -447
- webscout/Provider/Andi.py +0 -228
- webscout/Provider/Blackboxai.py +0 -229
- webscout/Provider/ChatGPTClone.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/ChatSandbox.py +0 -342
- webscout/Provider/Cloudflare.py +0 -325
- webscout/Provider/Cohere.py +0 -208
- webscout/Provider/Deepinfra.py +0 -338
- webscout/Provider/ElectronHub.py +0 -773
- webscout/Provider/ExaAI.py +0 -261
- webscout/Provider/ExaChat.py +0 -358
- webscout/Provider/Free2GPT.py +0 -241
- webscout/Provider/GPTWeb.py +0 -249
- webscout/Provider/Gemini.py +0 -169
- webscout/Provider/GithubChat.py +0 -370
- webscout/Provider/GizAI.py +0 -285
- webscout/Provider/Glider.py +0 -222
- webscout/Provider/Groq.py +0 -801
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HeckAI.py +0 -257
- webscout/Provider/HuggingFaceChat.py +0 -469
- webscout/Provider/Hunyuan.py +0 -283
- webscout/Provider/Jadve.py +0 -291
- webscout/Provider/Koboldai.py +0 -381
- webscout/Provider/LambdaChat.py +0 -411
- webscout/Provider/Llama3.py +0 -259
- webscout/Provider/MCPCore.py +0 -315
- webscout/Provider/Marcus.py +0 -206
- webscout/Provider/Nemotron.py +0 -218
- webscout/Provider/Netwrck.py +0 -270
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/__init__.py +0 -28
- webscout/Provider/OPENAI/ai4chat.py +0 -286
- webscout/Provider/OPENAI/base.py +0 -46
- webscout/Provider/OPENAI/c4ai.py +0 -367
- webscout/Provider/OPENAI/chatgpt.py +0 -549
- webscout/Provider/OPENAI/chatgptclone.py +0 -481
- webscout/Provider/OPENAI/deepinfra.py +0 -309
- webscout/Provider/OPENAI/e2b.py +0 -1350
- webscout/Provider/OPENAI/exaai.py +0 -404
- webscout/Provider/OPENAI/exachat.py +0 -437
- webscout/Provider/OPENAI/freeaichat.py +0 -352
- webscout/Provider/OPENAI/glider.py +0 -316
- webscout/Provider/OPENAI/groq.py +0 -354
- webscout/Provider/OPENAI/heckai.py +0 -341
- webscout/Provider/OPENAI/llmchatco.py +0 -327
- webscout/Provider/OPENAI/mcpcore.py +0 -376
- webscout/Provider/OPENAI/multichat.py +0 -368
- webscout/Provider/OPENAI/netwrck.py +0 -350
- webscout/Provider/OPENAI/opkfc.py +0 -488
- webscout/Provider/OPENAI/scirachat.py +0 -462
- webscout/Provider/OPENAI/sonus.py +0 -294
- webscout/Provider/OPENAI/standardinput.py +0 -425
- webscout/Provider/OPENAI/textpollinations.py +0 -329
- webscout/Provider/OPENAI/toolbaz.py +0 -406
- webscout/Provider/OPENAI/typegpt.py +0 -346
- webscout/Provider/OPENAI/uncovrAI.py +0 -455
- webscout/Provider/OPENAI/utils.py +0 -211
- webscout/Provider/OPENAI/venice.py +0 -413
- webscout/Provider/OPENAI/wisecat.py +0 -381
- webscout/Provider/OPENAI/writecream.py +0 -156
- webscout/Provider/OPENAI/x0gpt.py +0 -371
- webscout/Provider/OPENAI/yep.py +0 -327
- webscout/Provider/OpenGPT.py +0 -209
- webscout/Provider/Openai.py +0 -496
- webscout/Provider/PI.py +0 -429
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/QwenLM.py +0 -254
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -290
- 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/__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/__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/__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/__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/__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/__init__.py +0 -12
- 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/__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/__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/__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/__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/__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/__init__.py +0 -4
- webscout/Provider/TTI/talkai/async_talkai.py +0 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
- webscout/Provider/TTS/__init__.py +0 -8
- webscout/Provider/TTS/base.py +0 -159
- webscout/Provider/TTS/deepgram.py +0 -156
- webscout/Provider/TTS/elevenlabs.py +0 -111
- webscout/Provider/TTS/gesserit.py +0 -128
- webscout/Provider/TTS/murfai.py +0 -113
- webscout/Provider/TTS/parler.py +0 -111
- webscout/Provider/TTS/speechma.py +0 -180
- webscout/Provider/TTS/streamElements.py +0 -333
- webscout/Provider/TTS/utils.py +0 -280
- webscout/Provider/TeachAnything.py +0 -233
- webscout/Provider/TextPollinationsAI.py +0 -306
- webscout/Provider/TwoAI.py +0 -280
- webscout/Provider/TypliAI.py +0 -305
- webscout/Provider/Venice.py +0 -258
- webscout/Provider/VercelAI.py +0 -253
- webscout/Provider/WiseCat.py +0 -233
- webscout/Provider/WrDoChat.py +0 -370
- webscout/Provider/Writecream.py +0 -237
- webscout/Provider/WritingMate.py +0 -269
- webscout/Provider/Youchat.py +0 -330
- webscout/Provider/__init__.py +0 -178
- webscout/Provider/ai4chat.py +0 -203
- webscout/Provider/aimathgpt.py +0 -189
- webscout/Provider/akashgpt.py +0 -335
- webscout/Provider/asksteve.py +0 -212
- webscout/Provider/bagoodex.py +0 -145
- webscout/Provider/cerebras.py +0 -288
- webscout/Provider/chatglm.py +0 -215
- webscout/Provider/cleeai.py +0 -213
- webscout/Provider/copilot.py +0 -425
- webscout/Provider/elmo.py +0 -283
- webscout/Provider/freeaichat.py +0 -285
- webscout/Provider/geminiapi.py +0 -208
- webscout/Provider/geminiprorealtime.py +0 -160
- webscout/Provider/granite.py +0 -235
- webscout/Provider/hermes.py +0 -266
- webscout/Provider/julius.py +0 -223
- webscout/Provider/koala.py +0 -268
- webscout/Provider/learnfastai.py +0 -325
- webscout/Provider/llama3mitril.py +0 -215
- webscout/Provider/llmchat.py +0 -255
- webscout/Provider/llmchatco.py +0 -306
- webscout/Provider/meta.py +0 -798
- webscout/Provider/multichat.py +0 -364
- webscout/Provider/scira_chat.py +0 -297
- webscout/Provider/scnet.py +0 -243
- webscout/Provider/searchchat.py +0 -292
- webscout/Provider/sonus.py +0 -258
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/toolbaz.py +0 -353
- webscout/Provider/turboseek.py +0 -266
- webscout/Provider/typefully.py +0 -330
- webscout/Provider/typegpt.py +0 -289
- webscout/Provider/uncovr.py +0 -368
- webscout/Provider/x0gpt.py +0 -299
- webscout/Provider/yep.py +0 -389
- webscout/litagent/__init__.py +0 -29
- webscout/litagent/agent.py +0 -455
- webscout/litagent/constants.py +0 -60
- webscout/litprinter/__init__.py +0 -59
- webscout/scout/__init__.py +0 -8
- webscout/scout/core/__init__.py +0 -7
- webscout/scout/core/crawler.py +0 -140
- webscout/scout/core/scout.py +0 -568
- webscout/scout/core/search_result.py +0 -96
- webscout/scout/core/text_analyzer.py +0 -63
- webscout/scout/core/text_utils.py +0 -277
- webscout/scout/core/web_analyzer.py +0 -52
- webscout/scout/core.py +0 -881
- webscout/scout/element.py +0 -460
- webscout/scout/parsers/__init__.py +0 -69
- webscout/scout/parsers/html5lib_parser.py +0 -172
- webscout/scout/parsers/html_parser.py +0 -236
- webscout/scout/parsers/lxml_parser.py +0 -178
- webscout/scout/utils.py +0 -37
- webscout/swiftcli/__init__.py +0 -95
- webscout/swiftcli/core/__init__.py +0 -7
- webscout/swiftcli/core/cli.py +0 -297
- webscout/swiftcli/core/context.py +0 -104
- webscout/swiftcli/core/group.py +0 -241
- webscout/swiftcli/decorators/__init__.py +0 -28
- webscout/swiftcli/decorators/command.py +0 -221
- webscout/swiftcli/decorators/options.py +0 -220
- webscout/swiftcli/decorators/output.py +0 -252
- webscout/swiftcli/exceptions.py +0 -21
- webscout/swiftcli/plugins/__init__.py +0 -9
- webscout/swiftcli/plugins/base.py +0 -135
- webscout/swiftcli/plugins/manager.py +0 -262
- webscout/swiftcli/utils/__init__.py +0 -59
- webscout/swiftcli/utils/formatting.py +0 -252
- webscout/swiftcli/utils/parsing.py +0 -267
- webscout/zeroart/__init__.py +0 -55
- webscout/zeroart/base.py +0 -60
- webscout/zeroart/effects.py +0 -99
- webscout/zeroart/fonts.py +0 -816
- webscout-8.2.6.dist-info/RECORD +0 -307
- webscout-8.2.6.dist-info/entry_points.txt +0 -3
- webscout-8.2.6.dist-info/top_level.txt +0 -2
- webstoken/__init__.py +0 -30
- webstoken/classifier.py +0 -189
- webstoken/keywords.py +0 -216
- webstoken/language.py +0 -128
- webstoken/ner.py +0 -164
- webstoken/normalizer.py +0 -35
- webstoken/processor.py +0 -77
- webstoken/sentiment.py +0 -206
- webstoken/stemmer.py +0 -73
- webstoken/tagger.py +0 -60
- webstoken/tokenizer.py +0 -158
- {webscout-8.2.6.dist-info → webscout-8.2.7.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -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
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import json
|
|
3
|
-
import socket
|
|
4
|
-
from typing import Optional, Dict, Any
|
|
5
|
-
import aiohttp
|
|
6
|
-
from ..core.level import LogLevel
|
|
7
|
-
|
|
8
|
-
class NetworkHandler:
|
|
9
|
-
"""Handler for sending log messages to a remote server."""
|
|
10
|
-
|
|
11
|
-
def __init__(
|
|
12
|
-
self,
|
|
13
|
-
host: str,
|
|
14
|
-
port: int,
|
|
15
|
-
protocol: str = "http",
|
|
16
|
-
endpoint: str = "/logs",
|
|
17
|
-
method: str = "POST",
|
|
18
|
-
headers: Optional[Dict[str, str]] = None,
|
|
19
|
-
timeout: float = 5.0,
|
|
20
|
-
level: LogLevel = LogLevel.DEBUG,
|
|
21
|
-
batch_size: int = 0,
|
|
22
|
-
retry_count: int = 3,
|
|
23
|
-
retry_delay: float = 1.0,
|
|
24
|
-
custom_fields: Optional[Dict[str, Any]] = None
|
|
25
|
-
):
|
|
26
|
-
"""
|
|
27
|
-
Initialize network handler.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
host: Remote server hostname/IP
|
|
31
|
-
port: Remote server port
|
|
32
|
-
protocol: 'http', 'https', or 'tcp'
|
|
33
|
-
endpoint: Server endpoint for HTTP/HTTPS
|
|
34
|
-
method: HTTP method to use
|
|
35
|
-
headers: Optional HTTP headers
|
|
36
|
-
timeout: Request timeout in seconds
|
|
37
|
-
level: Minimum log level to send
|
|
38
|
-
batch_size: Number of logs to batch (0 = no batching)
|
|
39
|
-
retry_count: Number of retries on failure
|
|
40
|
-
retry_delay: Delay between retries in seconds
|
|
41
|
-
custom_fields: Additional fields to include in log data
|
|
42
|
-
"""
|
|
43
|
-
self.host = host
|
|
44
|
-
self.port = port
|
|
45
|
-
self.protocol = protocol.lower()
|
|
46
|
-
self.endpoint = endpoint
|
|
47
|
-
self.method = method.upper()
|
|
48
|
-
self.headers = headers or {}
|
|
49
|
-
self.timeout = timeout
|
|
50
|
-
self.level = level
|
|
51
|
-
self.batch_size = batch_size
|
|
52
|
-
self.retry_count = retry_count
|
|
53
|
-
self.retry_delay = retry_delay
|
|
54
|
-
self.custom_fields = custom_fields or {}
|
|
55
|
-
|
|
56
|
-
if self.protocol not in ["http", "https", "tcp"]:
|
|
57
|
-
raise ValueError("Protocol must be 'http', 'https' or 'tcp'")
|
|
58
|
-
|
|
59
|
-
self._batch = []
|
|
60
|
-
self._tcp_socket = None
|
|
61
|
-
self._session = None
|
|
62
|
-
|
|
63
|
-
async def _init_session(self):
|
|
64
|
-
"""Initialize HTTP session if needed."""
|
|
65
|
-
if self.protocol in ["http", "https"] and not self._session:
|
|
66
|
-
self._session = aiohttp.ClientSession(
|
|
67
|
-
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
async def _send_http(self, data: Dict[str, Any]) -> bool:
|
|
71
|
-
"""Send log data via HTTP/HTTPS."""
|
|
72
|
-
await self._init_session()
|
|
73
|
-
|
|
74
|
-
url = f"{self.protocol}://{self.host}:{self.port}{self.endpoint}"
|
|
75
|
-
|
|
76
|
-
for attempt in range(self.retry_count + 1):
|
|
77
|
-
try:
|
|
78
|
-
async with self._session.request(
|
|
79
|
-
method=self.method,
|
|
80
|
-
url=url,
|
|
81
|
-
json=data,
|
|
82
|
-
headers=self.headers
|
|
83
|
-
) as response:
|
|
84
|
-
return response.status < 400
|
|
85
|
-
|
|
86
|
-
except Exception:
|
|
87
|
-
if attempt == self.retry_count:
|
|
88
|
-
return False
|
|
89
|
-
await asyncio.sleep(self.retry_delay)
|
|
90
|
-
|
|
91
|
-
async def _send_tcp(self, data: Dict[str, Any]) -> bool:
|
|
92
|
-
"""Send log data via TCP."""
|
|
93
|
-
message = json.dumps(data).encode() + b"\n"
|
|
94
|
-
|
|
95
|
-
for attempt in range(self.retry_count + 1):
|
|
96
|
-
try:
|
|
97
|
-
if not self._tcp_socket:
|
|
98
|
-
self._tcp_socket = socket.socket(
|
|
99
|
-
socket.AF_INET, socket.SOCK_STREAM
|
|
100
|
-
)
|
|
101
|
-
self._tcp_socket.settimeout(self.timeout)
|
|
102
|
-
self._tcp_socket.connect((self.host, self.port))
|
|
103
|
-
|
|
104
|
-
self._tcp_socket.sendall(message)
|
|
105
|
-
return True
|
|
106
|
-
|
|
107
|
-
except Exception:
|
|
108
|
-
if self._tcp_socket:
|
|
109
|
-
self._tcp_socket.close()
|
|
110
|
-
self._tcp_socket = None
|
|
111
|
-
|
|
112
|
-
if attempt == self.retry_count:
|
|
113
|
-
return False
|
|
114
|
-
|
|
115
|
-
await asyncio.sleep(self.retry_delay)
|
|
116
|
-
|
|
117
|
-
def emit(self, message: str, level: LogLevel):
|
|
118
|
-
"""
|
|
119
|
-
Synchronously send log message.
|
|
120
|
-
Not recommended - use async_emit instead.
|
|
121
|
-
"""
|
|
122
|
-
if level.value >= self.level.value:
|
|
123
|
-
loop = asyncio.get_event_loop()
|
|
124
|
-
loop.run_until_complete(self.async_emit(message, level))
|
|
125
|
-
|
|
126
|
-
async def async_emit(self, message: str, level: LogLevel):
|
|
127
|
-
"""Asynchronously send log message to remote server."""
|
|
128
|
-
# Fix: Allow all messages if level is NOTSET
|
|
129
|
-
if self.level == LogLevel.NOTSET or level.value >= self.level.value:
|
|
130
|
-
log_data = {
|
|
131
|
-
"message": message,
|
|
132
|
-
"level": level.name,
|
|
133
|
-
**self.custom_fields
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if self.batch_size > 0:
|
|
137
|
-
self._batch.append(log_data)
|
|
138
|
-
if len(self._batch) >= self.batch_size:
|
|
139
|
-
await self._send_batch()
|
|
140
|
-
else:
|
|
141
|
-
if self.protocol in ["http", "https"]:
|
|
142
|
-
await self._send_http(log_data)
|
|
143
|
-
else:
|
|
144
|
-
await self._send_tcp(log_data)
|
|
145
|
-
|
|
146
|
-
async def _send_batch(self):
|
|
147
|
-
"""Send batched log messages."""
|
|
148
|
-
if not self._batch:
|
|
149
|
-
return
|
|
150
|
-
|
|
151
|
-
batch_data = {"logs": self._batch}
|
|
152
|
-
success = False
|
|
153
|
-
|
|
154
|
-
if self.protocol in ["http", "https"]:
|
|
155
|
-
success = await self._send_http(batch_data)
|
|
156
|
-
else:
|
|
157
|
-
success = await self._send_tcp(batch_data)
|
|
158
|
-
|
|
159
|
-
if success:
|
|
160
|
-
self._batch.clear()
|
|
161
|
-
|
|
162
|
-
async def close(self):
|
|
163
|
-
"""Close network connections."""
|
|
164
|
-
if self._batch:
|
|
165
|
-
await self._send_batch()
|
|
166
|
-
|
|
167
|
-
if self._tcp_socket:
|
|
168
|
-
self._tcp_socket.close()
|
|
169
|
-
self._tcp_socket = None
|
|
170
|
-
|
|
171
|
-
if self._session:
|
|
172
|
-
await self._session.close()
|
|
173
|
-
self._session = None
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
# Terminal Color Management System
|
|
2
|
-
# Provides extensive ANSI color support with themes, gradients, and animations
|
|
3
|
-
|
|
4
|
-
from typing import Optional
|
|
5
|
-
from ..core.level import LogLevel
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class LogColors:
|
|
9
|
-
"""
|
|
10
|
-
Comprehensive terminal color and styling system with support for:
|
|
11
|
-
- Basic and bright colors (foreground/background)
|
|
12
|
-
- RGB and 256-color modes
|
|
13
|
-
- Text styles (bold, italic, etc.)
|
|
14
|
-
- Gradients and animations
|
|
15
|
-
- Predefined themes
|
|
16
|
-
"""
|
|
17
|
-
# Reset
|
|
18
|
-
RESET = "\033[0m"
|
|
19
|
-
|
|
20
|
-
# Regular colors
|
|
21
|
-
BLACK = "\033[30m"
|
|
22
|
-
RED = "\033[31m"
|
|
23
|
-
GREEN = "\033[32m"
|
|
24
|
-
YELLOW = "\033[33m"
|
|
25
|
-
BLUE = "\033[34m"
|
|
26
|
-
MAGENTA = "\033[35m"
|
|
27
|
-
CYAN = "\033[36m"
|
|
28
|
-
WHITE = "\033[37m"
|
|
29
|
-
|
|
30
|
-
# Background colors
|
|
31
|
-
BG_BLACK = "\033[40m"
|
|
32
|
-
BG_RED = "\033[41m"
|
|
33
|
-
BG_GREEN = "\033[42m"
|
|
34
|
-
BG_YELLOW = "\033[43m"
|
|
35
|
-
BG_BLUE = "\033[44m"
|
|
36
|
-
BG_MAGENTA = "\033[45m"
|
|
37
|
-
BG_CYAN = "\033[46m"
|
|
38
|
-
BG_WHITE = "\033[47m"
|
|
39
|
-
|
|
40
|
-
# Bright colors
|
|
41
|
-
BRIGHT_BLACK = "\033[90m"
|
|
42
|
-
BRIGHT_RED = "\033[91m"
|
|
43
|
-
BRIGHT_GREEN = "\033[92m"
|
|
44
|
-
BRIGHT_YELLOW = "\033[93m"
|
|
45
|
-
BRIGHT_BLUE = "\033[94m"
|
|
46
|
-
BRIGHT_MAGENTA = "\033[95m"
|
|
47
|
-
BRIGHT_CYAN = "\033[96m"
|
|
48
|
-
BRIGHT_WHITE = "\033[97m"
|
|
49
|
-
|
|
50
|
-
# Bright background colors
|
|
51
|
-
BG_BRIGHT_BLACK = "\033[100m"
|
|
52
|
-
BG_BRIGHT_RED = "\033[101m"
|
|
53
|
-
BG_BRIGHT_GREEN = "\033[102m"
|
|
54
|
-
BG_BRIGHT_YELLOW = "\033[103m"
|
|
55
|
-
BG_BRIGHT_BLUE = "\033[104m"
|
|
56
|
-
BG_BRIGHT_MAGENTA = "\033[105m"
|
|
57
|
-
BG_BRIGHT_CYAN = "\033[106m"
|
|
58
|
-
BG_BRIGHT_WHITE = "\033[107m"
|
|
59
|
-
|
|
60
|
-
# Custom theme colors using RGB
|
|
61
|
-
FIRE = "\033[38;2;255;100;0m" # Orange-red
|
|
62
|
-
ICE = "\033[38;2;150;230;255m" # Light blue
|
|
63
|
-
GRASS = "\033[38;2;120;200;80m" # Light green
|
|
64
|
-
PURPLE = "\033[38;2;147;112;219m" # Medium purple
|
|
65
|
-
GOLD = "\033[38;2;255;215;0m" # Golden
|
|
66
|
-
|
|
67
|
-
# Gradient colors
|
|
68
|
-
SUNSET_START = "\033[38;2;255;128;0m" # Orange
|
|
69
|
-
SUNSET_END = "\033[38;2;255;51;153m" # Pink
|
|
70
|
-
|
|
71
|
-
OCEAN_START = "\033[38;2;0;204;255m" # Light blue
|
|
72
|
-
OCEAN_END = "\033[38;2;0;51;102m" # Dark blue
|
|
73
|
-
|
|
74
|
-
FOREST_START = "\033[38;2;34;139;34m" # Forest green
|
|
75
|
-
FOREST_END = "\033[38;2;0;100;0m" # Dark green
|
|
76
|
-
|
|
77
|
-
# Bold
|
|
78
|
-
BOLD = "\033[1m"
|
|
79
|
-
|
|
80
|
-
# Styles
|
|
81
|
-
DIM = "\033[2m"
|
|
82
|
-
ITALIC = "\033[3m"
|
|
83
|
-
UNDERLINE = "\033[4m"
|
|
84
|
-
|
|
85
|
-
# Level color mapping
|
|
86
|
-
LEVEL_COLORS = {
|
|
87
|
-
LogLevel.DEBUG: BRIGHT_BLACK,
|
|
88
|
-
LogLevel.INFO: BRIGHT_BLUE,
|
|
89
|
-
LogLevel.WARNING: BRIGHT_YELLOW,
|
|
90
|
-
LogLevel.ERROR: BRIGHT_RED,
|
|
91
|
-
LogLevel.CRITICAL: BRIGHT_RED + BOLD,
|
|
92
|
-
LogLevel.NOTSET: WHITE
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
# Add style combinations
|
|
96
|
-
STYLES = {
|
|
97
|
-
"bold": "\033[1m",
|
|
98
|
-
"dim": "\033[2m",
|
|
99
|
-
"italic": "\033[3m",
|
|
100
|
-
"underline": "\033[4m",
|
|
101
|
-
"blink": "\033[5m",
|
|
102
|
-
"reverse": "\033[7m",
|
|
103
|
-
"hidden": "\033[8m",
|
|
104
|
-
"strike": "\033[9m",
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
@staticmethod
|
|
108
|
-
def rgb(r: int, g: int, b: int, background: bool = False) -> str:
|
|
109
|
-
"""Create RGB color code."""
|
|
110
|
-
if background:
|
|
111
|
-
return f"\033[48;2;{r};{g};{b}m"
|
|
112
|
-
return f"\033[38;2;{r};{g};{b}m"
|
|
113
|
-
|
|
114
|
-
@staticmethod
|
|
115
|
-
def color_256(code: int, background: bool = False) -> str:
|
|
116
|
-
"""Create 256-color code."""
|
|
117
|
-
if background:
|
|
118
|
-
return f"\033[48;5;{code}m"
|
|
119
|
-
return f"\033[38;5;{code}m"
|
|
120
|
-
|
|
121
|
-
@staticmethod
|
|
122
|
-
def combine(*colors):
|
|
123
|
-
return "".join(colors)
|
|
124
|
-
|
|
125
|
-
@staticmethod
|
|
126
|
-
def gradient(text: str, start_rgb: tuple, end_rgb: tuple) -> str:
|
|
127
|
-
"""Create a gradient effect across text."""
|
|
128
|
-
if len(text) < 2:
|
|
129
|
-
return LogColors.rgb(*start_rgb) + text
|
|
130
|
-
|
|
131
|
-
result = []
|
|
132
|
-
for i, char in enumerate(text):
|
|
133
|
-
r = int(start_rgb[0] + (end_rgb[0] - start_rgb[0]) * i / (len(text) - 1))
|
|
134
|
-
g = int(start_rgb[1] + (end_rgb[1] - start_rgb[1]) * i / (len(text) - 1))
|
|
135
|
-
b = int(start_rgb[2] + (end_rgb[2] - start_rgb[2]) * i / (len(text) - 1))
|
|
136
|
-
result.append(f"{LogColors.rgb(r, g, b)}{char}")
|
|
137
|
-
|
|
138
|
-
return "".join(result) + LogColors.RESET
|
|
139
|
-
|
|
140
|
-
@staticmethod
|
|
141
|
-
def rainbow(text: str) -> str:
|
|
142
|
-
"""Apply rainbow colors to text."""
|
|
143
|
-
colors = [
|
|
144
|
-
(255, 0, 0), # Red
|
|
145
|
-
(255, 127, 0), # Orange
|
|
146
|
-
(255, 255, 0), # Yellow
|
|
147
|
-
(0, 255, 0), # Green
|
|
148
|
-
(0, 0, 255), # Blue
|
|
149
|
-
(75, 0, 130), # Indigo
|
|
150
|
-
(148, 0, 211) # Violet
|
|
151
|
-
]
|
|
152
|
-
|
|
153
|
-
result = []
|
|
154
|
-
for i, char in enumerate(text):
|
|
155
|
-
color = colors[i % len(colors)]
|
|
156
|
-
result.append(f"{LogColors.rgb(*color)}{char}")
|
|
157
|
-
|
|
158
|
-
return "".join(result) + LogColors.RESET
|
|
159
|
-
|
|
160
|
-
@staticmethod
|
|
161
|
-
def animate(text: str, effect: str = "blink") -> str:
|
|
162
|
-
"""Apply animation effects to text."""
|
|
163
|
-
effects = {
|
|
164
|
-
"blink": LogColors.BLINK,
|
|
165
|
-
"reverse": LogColors.REVERSE,
|
|
166
|
-
"bold_blink": LogColors.combine(LogColors.BOLD, LogColors.BLINK),
|
|
167
|
-
"frame_blink": LogColors.combine(LogColors.FRAME, LogColors.BLINK),
|
|
168
|
-
"encircle_blink": LogColors.combine(LogColors.ENCIRCLE, LogColors.BLINK)
|
|
169
|
-
}
|
|
170
|
-
return effects.get(effect, LogColors.BLINK) + text + LogColors.RESET
|
|
171
|
-
|
|
172
|
-
@staticmethod
|
|
173
|
-
def style_text(text: str, *styles: str, color: Optional[str] = None) -> str:
|
|
174
|
-
"""Apply multiple styles and color to text."""
|
|
175
|
-
style_codes = "".join(LogColors.STYLES.get(style, "") for style in styles)
|
|
176
|
-
color_code = color if color else ""
|
|
177
|
-
return f"{style_codes}{color_code}{text}{LogColors.RESET}"
|
|
178
|
-
|
|
179
|
-
@staticmethod
|
|
180
|
-
def level_style(level: LogLevel, text: str) -> str:
|
|
181
|
-
"""Style text according to log level with enhanced formatting."""
|
|
182
|
-
color = LogColors.LEVEL_COLORS.get(level, LogColors.RESET)
|
|
183
|
-
|
|
184
|
-
# Apply appropriate styling based on level
|
|
185
|
-
if level == LogLevel.CRITICAL:
|
|
186
|
-
return f"{color}{LogColors.STYLES['bold']}{text}{LogColors.RESET}"
|
|
187
|
-
elif level == LogLevel.ERROR:
|
|
188
|
-
return f"{color}{text}{LogColors.RESET}"
|
|
189
|
-
elif level == LogLevel.WARNING:
|
|
190
|
-
return f"{color}{text}{LogColors.RESET}"
|
|
191
|
-
else:
|
|
192
|
-
return f"{color}{text}{LogColors.RESET}"
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
class LogTheme:
|
|
196
|
-
"""Pre-defined color themes and combinations."""
|
|
197
|
-
|
|
198
|
-
@staticmethod
|
|
199
|
-
def get_theme(name: str) -> str:
|
|
200
|
-
themes = {
|
|
201
|
-
# Status themes
|
|
202
|
-
"success": LogColors.combine(LogColors.BRIGHT_GREEN, LogColors.BOLD),
|
|
203
|
-
"error": LogColors.combine(LogColors.BRIGHT_RED, LogColors.BOLD),
|
|
204
|
-
"warning": LogColors.combine(LogColors.BRIGHT_YELLOW, LogColors.BOLD),
|
|
205
|
-
"info": LogColors.combine(LogColors.BRIGHT_BLUE, LogColors.BOLD),
|
|
206
|
-
"debug": LogColors.combine(LogColors.DIM, LogColors.WHITE),
|
|
207
|
-
"critical": LogColors.combine(LogColors.BG_RED, LogColors.WHITE, LogColors.BOLD),
|
|
208
|
-
|
|
209
|
-
# Special themes
|
|
210
|
-
"header": LogColors.combine(LogColors.BRIGHT_WHITE, LogColors.BOLD, LogColors.UNDERLINE),
|
|
211
|
-
"highlight": LogColors.combine(LogColors.BLACK, LogColors.BG_BRIGHT_YELLOW),
|
|
212
|
-
"important": LogColors.combine(LogColors.BRIGHT_RED, LogColors.BOLD, LogColors.UNDERLINE),
|
|
213
|
-
"subtle": LogColors.combine(LogColors.DIM, LogColors.BRIGHT_BLACK),
|
|
214
|
-
|
|
215
|
-
# UI themes
|
|
216
|
-
"title": LogColors.combine(LogColors.BRIGHT_WHITE, LogColors.BOLD, LogColors.UNDERLINE),
|
|
217
|
-
"subtitle": LogColors.combine(LogColors.BRIGHT_WHITE, LogColors.DIM),
|
|
218
|
-
"link": LogColors.combine(LogColors.BLUE, LogColors.UNDERLINE),
|
|
219
|
-
"code": LogColors.combine(LogColors.BRIGHT_BLACK, LogColors.BG_WHITE),
|
|
220
|
-
|
|
221
|
-
# Data themes
|
|
222
|
-
"number": LogColors.combine(LogColors.BRIGHT_CYAN, LogColors.BOLD),
|
|
223
|
-
"string": LogColors.combine(LogColors.BRIGHT_GREEN),
|
|
224
|
-
"boolean": LogColors.combine(LogColors.BRIGHT_YELLOW, LogColors.BOLD),
|
|
225
|
-
"null": LogColors.combine(LogColors.DIM, LogColors.ITALIC),
|
|
226
|
-
|
|
227
|
-
# Custom themes
|
|
228
|
-
"fire": LogColors.combine(LogColors.FIRE, LogColors.BOLD),
|
|
229
|
-
"ice": LogColors.combine(LogColors.ICE, LogColors.BOLD),
|
|
230
|
-
"nature": LogColors.combine(LogColors.GRASS, LogColors.BOLD),
|
|
231
|
-
"royal": LogColors.combine(LogColors.PURPLE, LogColors.BOLD),
|
|
232
|
-
"precious": LogColors.combine(LogColors.GOLD, LogColors.BOLD),
|
|
233
|
-
|
|
234
|
-
# Gradient themes
|
|
235
|
-
"sunset": LogColors.combine(LogColors.SUNSET_START, LogColors.SUNSET_END),
|
|
236
|
-
"ocean": LogColors.combine(LogColors.OCEAN_START, LogColors.OCEAN_END),
|
|
237
|
-
"forest": LogColors.combine(LogColors.FOREST_START, LogColors.FOREST_END),
|
|
238
|
-
}
|
|
239
|
-
return themes.get(name, LogColors.RESET)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
# Define level colors after both classes are defined
|
|
243
|
-
LogColors.LEVEL_COLORS = {
|
|
244
|
-
"DEBUG": LogTheme.get_theme("debug"),
|
|
245
|
-
"INFO": LogTheme.get_theme("info"),
|
|
246
|
-
"WARNING": LogTheme.get_theme("warning"),
|
|
247
|
-
"ERROR": LogTheme.get_theme("error"),
|
|
248
|
-
"CRITICAL": LogTheme.get_theme("critical")
|
|
249
|
-
}
|