webscout 7.2__py3-none-any.whl → 7.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/Bard.py CHANGED
@@ -16,7 +16,7 @@ import httpx
16
16
  from httpx import AsyncClient, HTTPStatusError
17
17
 
18
18
  # For image models using validation. Adjust based on organization internal pydantic.
19
- from pydantic import BaseModel, field_validator
19
+ from pydantic import BaseModel, validator
20
20
 
21
21
  # Rich is retained for logging within image methods.
22
22
  from rich.console import Console
@@ -463,7 +463,7 @@ class GeneratedImage(Image):
463
463
  """
464
464
  cookies: Dict[str, str]
465
465
 
466
- @field_validator("cookies")
466
+ @validator("cookies")
467
467
  def validate_cookies(cls, v):
468
468
  if not v:
469
469
  raise ValueError("GeneratedImage requires cookies from GeminiClient.")
@@ -1,6 +1,7 @@
1
1
  from enum import Enum
2
2
 
3
3
  class LogLevel(Enum):
4
+ NOTSET = 0
4
5
  DEBUG = 10
5
6
  INFO = 20
6
7
  WARNING = 30
@@ -9,6 +10,8 @@ class LogLevel(Enum):
9
10
 
10
11
  @staticmethod
11
12
  def get_level(level_str: str) -> 'LogLevel':
13
+ if not level_str:
14
+ return LogLevel.NOTSET
12
15
  try:
13
16
  return LogLevel[level_str.upper()]
14
17
  except KeyError:
@@ -1,12 +1,14 @@
1
1
 
2
2
  import asyncio
3
- import json
3
+ import sys
4
+ import threading
5
+ import traceback
4
6
  from datetime import datetime
5
7
  from typing import Any, Dict, List, Optional, Union
6
8
 
7
- from ..styles.colors import LogColors
9
+ from ..core.level import LogLevel
8
10
  from ..styles.formats import LogFormat
9
- from .level import LogLevel
11
+ from ..styles.colors import LogColors
10
12
 
11
13
  class Logger:
12
14
  # Emoji mappings for different log levels
@@ -21,91 +23,125 @@ class Logger:
21
23
  def __init__(
22
24
  self,
23
25
  name: str = "LitLogger",
24
- level: Union[str, LogLevel] = LogLevel.INFO,
25
- format: str = LogFormat.MODERN,
26
+ level: Union[str, LogLevel, None] = None,
27
+ format: Union[str, LogFormat] = LogFormat.MODERN_EMOJI,
26
28
  handlers: List = None,
27
29
  enable_colors: bool = True,
28
- async_mode: bool = False
30
+ async_mode: bool = False,
31
+ show_thread: bool = True,
32
+ show_context: bool = True
29
33
  ):
30
34
  self.name = name
31
- self.level = LogLevel.get_level(level) if isinstance(level, str) else level
35
+ self.level = LogLevel.NOTSET if level is None else (
36
+ LogLevel.get_level(level) if isinstance(level, str) else level
37
+ )
32
38
  self.format = format
33
- self.handlers = handlers or []
34
39
  self.enable_colors = enable_colors
35
40
  self.async_mode = async_mode
41
+ self.show_thread = show_thread
42
+ self.show_context = show_context
36
43
  self._context_data = {}
37
- self._metrics = {}
44
+
45
+ # Initialize with default console handler if none provided
46
+ if handlers is None:
47
+ from ..handlers.console import ConsoleHandler
48
+ self.handlers = [ConsoleHandler(level=self.level)]
49
+ else:
50
+ self.handlers = handlers
38
51
 
39
52
  def _format_message(self, level: LogLevel, message: str, **kwargs) -> str:
40
53
  now = datetime.now()
54
+ emoji = self.LEVEL_EMOJIS.get(level, "") if self.enable_colors else ""
55
+
41
56
  log_data = {
42
- "timestamp": now.strftime("%Y-%m-%d %H:%M:%S"),
43
- "time": now.strftime("%H:%M:%S"), # Add time field
44
- "date": now.strftime("%Y-%m-%d"), # Add date field
57
+ "timestamp": now.strftime("%H:%M:%S"),
45
58
  "level": level.name,
46
59
  "name": self.name,
47
- "message": message,
48
- "emoji": self.LEVEL_EMOJIS.get(level, ""),
49
- **self._context_data,
50
- **kwargs
60
+ "message": str(message),
61
+ "emoji": emoji,
62
+ "thread": threading.current_thread().name if self.show_thread else "",
51
63
  }
52
-
53
- if self.format == LogFormat.JSON:
54
- return json.dumps(log_data)
55
64
 
56
- try:
57
- formatted = self.format.format(**log_data)
58
- except KeyError as e:
59
- # Fallback to a basic format if the specified format fails
60
- basic_format = "[{time}] {level}: {message}"
61
- formatted = basic_format.format(**log_data)
65
+ # Add context data
66
+ if self.show_context and self._context_data:
67
+ log_data.update(self._context_data)
62
68
 
63
- if self.enable_colors:
64
- color = LogColors.LEVEL_COLORS.get(level, LogColors.RESET)
65
- return f"{color}{formatted}{LogColors.RESET}"
66
- return formatted
67
-
68
- async def _async_log(self, level: LogLevel, message: str, **kwargs):
69
- if level.value < self.level.value:
70
- return
71
-
72
- formatted_message = self._format_message(level, message, **kwargs)
73
- tasks = []
74
- for handler in self.handlers:
75
- if hasattr(handler, 'async_emit'):
76
- tasks.append(handler.async_emit(formatted_message, level))
77
- else:
78
- tasks.append(asyncio.to_thread(handler.emit, formatted_message, level))
69
+ # Add extra kwargs
70
+ log_data.update(kwargs)
79
71
 
80
- await asyncio.gather(*tasks)
81
-
82
- def _sync_log(self, level: LogLevel, message: str, **kwargs):
83
- if level.value < self.level.value:
84
- return
85
-
86
- formatted_message = self._format_message(level, message, **kwargs)
87
- for handler in self.handlers:
88
- handler.emit(formatted_message, level)
72
+ # Format exception if present
73
+ if 'exc_info' in kwargs:
74
+ exc_info = kwargs['exc_info']
75
+ if exc_info:
76
+ exception_text = ''.join(traceback.format_exception(*exc_info))
77
+ log_data['message'] = f"{message}\n{exception_text}"
78
+
79
+ try:
80
+ base_message = f"{emoji} [{log_data['timestamp']}] {level.name} {log_data['message']}"
81
+ return base_message
82
+ except Exception as e:
83
+ return f"[{log_data['timestamp']}] {level.name}: {message}"
89
84
 
90
- def log(self, level: LogLevel, message: str, **kwargs):
85
+ def _log(self, level: LogLevel, message: str, **kwargs):
91
86
  if self.async_mode:
92
- return asyncio.create_task(self._async_log(level, message, **kwargs))
87
+ loop = asyncio.get_event_loop()
88
+ if loop.is_running():
89
+ return asyncio.create_task(self._async_log(level, message, **kwargs))
90
+ else:
91
+ return loop.run_until_complete(self._async_log(level, message, **kwargs))
93
92
  return self._sync_log(level, message, **kwargs)
94
93
 
95
94
  def debug(self, message: str, **kwargs):
96
- self.log(LogLevel.DEBUG, message, **kwargs)
95
+ self._log(LogLevel.DEBUG, message, **kwargs)
97
96
 
98
97
  def info(self, message: str, **kwargs):
99
- self.log(LogLevel.INFO, message, **kwargs)
98
+ self._log(LogLevel.INFO, message, **kwargs)
100
99
 
101
100
  def warning(self, message: str, **kwargs):
102
- self.log(LogLevel.WARNING, message, **kwargs)
101
+ self._log(LogLevel.WARNING, message, **kwargs)
103
102
 
104
103
  def error(self, message: str, **kwargs):
105
- self.log(LogLevel.ERROR, message, **kwargs)
104
+ self._log(LogLevel.ERROR, message, **kwargs)
106
105
 
107
106
  def critical(self, message: str, **kwargs):
108
- self.log(LogLevel.CRITICAL, message, **kwargs)
107
+ self._log(LogLevel.CRITICAL, message, **kwargs)
108
+
109
+ def exception(self, message: str, exc_info=True, **kwargs):
110
+ """
111
+ Log an exception with traceback.
112
+
113
+ Args:
114
+ message: The message to log
115
+ exc_info: If True, adds exception info to the message. Can also be a tuple (type, value, traceback)
116
+ **kwargs: Additional key-value pairs to log
117
+ """
118
+ if exc_info:
119
+ if not isinstance(exc_info, tuple):
120
+ exc_info = sys.exc_info()
121
+ kwargs['exc_info'] = exc_info
122
+ self.error(message, **kwargs)
123
+
124
+ def _sync_log(self, level: LogLevel, message: str, **kwargs):
125
+ if self._should_log(level):
126
+ formatted_message = self._format_message(level, message, **kwargs)
127
+ for handler in self.handlers:
128
+ if handler.level == LogLevel.NOTSET or level.value >= handler.level.value:
129
+ handler.emit(formatted_message, level)
130
+
131
+ async def _async_log(self, level: LogLevel, message: str, **kwargs):
132
+ if self._should_log(level):
133
+ formatted_message = self._format_message(level, message, **kwargs)
134
+ tasks = []
135
+ for handler in self.handlers:
136
+ if handler.level == LogLevel.NOTSET or level.value >= handler.level.value:
137
+ if hasattr(handler, 'async_emit'):
138
+ tasks.append(handler.async_emit(formatted_message, level))
139
+ else:
140
+ tasks.append(asyncio.to_thread(handler.emit, formatted_message, level))
141
+ await asyncio.gather(*tasks)
142
+
143
+ def _should_log(self, level: LogLevel) -> bool:
144
+ return self.level == LogLevel.NOTSET or level.value >= self.level.value
109
145
 
110
146
  def set_context(self, **kwargs):
111
147
  self._context_data.update(kwargs)
@@ -113,6 +149,13 @@ class Logger:
113
149
  def clear_context(self):
114
150
  self._context_data.clear()
115
151
 
152
+ def set_style(self, style: str):
153
+ """Set logger style format."""
154
+ if style in LogFormat.TEMPLATES:
155
+ self.format = LogFormat.TEMPLATES[style]
156
+ else:
157
+ raise ValueError(f"Unknown style: {style}")
158
+
116
159
  def __enter__(self):
117
160
  return self
118
161
 
@@ -120,4 +163,4 @@ class Logger:
120
163
  if exc_type is not None:
121
164
  self.error(f"Context exited with error: {exc_val}")
122
165
  return False
123
- return True
166
+ return True
@@ -1,40 +1,23 @@
1
1
  import sys
2
- from typing import Optional, TextIO
3
2
  from ..core.level import LogLevel
3
+ from ..styles.colors import LogColors
4
4
 
5
5
  class ConsoleHandler:
6
- """Handler for outputting log messages to the console."""
7
-
8
- def __init__(self,
9
- stream: Optional[TextIO] = None,
10
- level: LogLevel = LogLevel.DEBUG):
11
- """
12
- Initialize console handler.
13
-
14
- Args:
15
- stream: Output stream (defaults to sys.stdout)
16
- level: Minimum log level to output
17
- """
18
- self.stream = stream or sys.stdout
6
+ def __init__(self, level: LogLevel = LogLevel.DEBUG, stream=sys.stdout):
19
7
  self.level = level
20
-
8
+ self.stream = stream
9
+ self.colors = LogColors()
10
+
21
11
  def emit(self, message: str, level: LogLevel):
22
- """
23
- Write log message to console if level is sufficient.
24
-
25
- Args:
26
- message: Formatted log message
27
- level: Message log level
28
- """
29
- if level.value >= self.level.value:
30
- try:
31
- self.stream.write(message + "\n")
32
- self.stream.flush()
33
- except Exception as e:
34
- # Fallback to stderr on error
35
- sys.stderr.write(f"Error in ConsoleHandler: {e}\n")
36
- sys.stderr.write(message + "\n")
37
- sys.stderr.flush()
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)
38
21
 
39
22
  async def async_emit(self, message: str, level: LogLevel):
40
23
  """
@@ -125,25 +125,24 @@ class NetworkHandler:
125
125
 
126
126
  async def async_emit(self, message: str, level: LogLevel):
127
127
  """Asynchronously send log message to remote server."""
128
- if level.value < self.level.value:
129
- return
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
+ }
130
135
 
131
- log_data = {
132
- "message": message,
133
- "level": level.name,
134
- **self.custom_fields
135
- }
136
-
137
- if self.batch_size > 0:
138
- self._batch.append(log_data)
139
- if len(self._batch) >= self.batch_size:
140
- await self._send_batch()
141
- else:
142
- if self.protocol in ["http", "https"]:
143
- await self._send_http(log_data)
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()
144
140
  else:
145
- await self._send_tcp(log_data)
146
-
141
+ if self.protocol in ["http", "https"]:
142
+ await self._send_http(log_data)
143
+ else:
144
+ await self._send_tcp(log_data)
145
+
147
146
  async def _send_batch(self):
148
147
  """Send batched log messages."""
149
148
  if not self._batch:
@@ -1,6 +1,10 @@
1
1
  # Terminal Color Management System
2
2
  # Provides extensive ANSI color support with themes, gradients, and animations
3
3
 
4
+ from typing import Optional
5
+ from ..core.level import LogLevel
6
+
7
+
4
8
  class LogColors:
5
9
  """
6
10
  Comprehensive terminal color and styling system with support for:
@@ -10,7 +14,10 @@ class LogColors:
10
14
  - Gradients and animations
11
15
  - Predefined themes
12
16
  """
13
- # Basic foreground colors
17
+ # Reset
18
+ RESET = "\033[0m"
19
+
20
+ # Regular colors
14
21
  BLACK = "\033[30m"
15
22
  RED = "\033[31m"
16
23
  GREEN = "\033[32m"
@@ -19,9 +26,8 @@ class LogColors:
19
26
  MAGENTA = "\033[35m"
20
27
  CYAN = "\033[36m"
21
28
  WHITE = "\033[37m"
22
- RESET = "\033[0m"
23
-
24
- # Basic background colors
29
+
30
+ # Background colors
25
31
  BG_BLACK = "\033[40m"
26
32
  BG_RED = "\033[41m"
27
33
  BG_GREEN = "\033[42m"
@@ -30,8 +36,8 @@ class LogColors:
30
36
  BG_MAGENTA = "\033[45m"
31
37
  BG_CYAN = "\033[46m"
32
38
  BG_WHITE = "\033[47m"
33
-
34
- # Bright foreground colors
39
+
40
+ # Bright colors
35
41
  BRIGHT_BLACK = "\033[90m"
36
42
  BRIGHT_RED = "\033[91m"
37
43
  BRIGHT_GREEN = "\033[92m"
@@ -40,7 +46,7 @@ class LogColors:
40
46
  BRIGHT_MAGENTA = "\033[95m"
41
47
  BRIGHT_CYAN = "\033[96m"
42
48
  BRIGHT_WHITE = "\033[97m"
43
-
49
+
44
50
  # Bright background colors
45
51
  BG_BRIGHT_BLACK = "\033[100m"
46
52
  BG_BRIGHT_RED = "\033[101m"
@@ -50,62 +56,53 @@ class LogColors:
50
56
  BG_BRIGHT_MAGENTA = "\033[105m"
51
57
  BG_BRIGHT_CYAN = "\033[106m"
52
58
  BG_BRIGHT_WHITE = "\033[107m"
53
-
54
- # Text styles
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
55
78
  BOLD = "\033[1m"
79
+
80
+ # Styles
56
81
  DIM = "\033[2m"
57
82
  ITALIC = "\033[3m"
58
83
  UNDERLINE = "\033[4m"
59
- BLINK = "\033[5m"
60
- REVERSE = "\033[7m"
61
- HIDDEN = "\033[8m"
62
- STRIKE = "\033[9m"
63
- DOUBLE_UNDERLINE = "\033[21m"
64
-
65
- # Special effects
66
- FRAME = "\033[51m"
67
- ENCIRCLE = "\033[52m"
68
- OVERLINE = "\033[53m"
69
-
70
- # Extended color combinations
71
- FIRE = "\033[38;2;255;100;0m"
72
- ICE = "\033[38;2;150;230;255m"
73
- GRASS = "\033[38;2;0;200;0m"
74
- PURPLE = "\033[38;2;160;32;240m"
75
- GOLD = "\033[38;2;255;215;0m"
76
- SILVER = "\033[38;2;192;192;192m"
77
- BRONZE = "\033[38;2;205;127;50m"
78
- PINK = "\033[38;2;255;192;203m"
79
- TEAL = "\033[38;2;0;128;128m"
80
- ORANGE = "\033[38;2;255;165;0m"
81
- BROWN = "\033[38;2;165;42;42m"
82
- MAROON = "\033[38;2;128;0;0m"
83
- NAVY = "\033[38;2;0;0;128m"
84
- OLIVE = "\033[38;2;128;128;0m"
85
-
86
- # Gradient colors
87
- SUNSET_START = "\033[38;2;255;128;0m"
88
- SUNSET_END = "\033[38;2;255;0;128m"
89
- OCEAN_START = "\033[38;2;0;255;255m"
90
- OCEAN_END = "\033[38;2;0;0;255m"
91
- FOREST_START = "\033[38;2;34;139;34m"
92
- FOREST_END = "\033[38;2;0;100;0m"
93
-
94
- # Special background combinations
95
- BG_FIRE = "\033[48;2;255;100;0m"
96
- BG_ICE = "\033[48;2;150;230;255m"
97
- BG_GRASS = "\033[48;2;0;200;0m"
98
- BG_PURPLE = "\033[48;2;160;32;240m"
99
- BG_GOLD = "\033[48;2;255;215;0m"
100
- BG_SILVER = "\033[48;2;192;192;192m"
101
- BG_BRONZE = "\033[48;2;205;127;50m"
102
- BG_PINK = "\033[48;2;255;192;203m"
103
- BG_TEAL = "\033[48;2;0;128;128m"
104
- BG_ORANGE = "\033[48;2;255;165;0m"
105
- BG_BROWN = "\033[48;2;165;42;42m"
106
- BG_MAROON = "\033[48;2;128;0;0m"
107
- BG_NAVY = "\033[48;2;0;0;128m"
108
- BG_OLIVE = "\033[48;2;128;128;0m"
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
+ }
109
106
 
110
107
  @staticmethod
111
108
  def rgb(r: int, g: int, b: int, background: bool = False) -> str:
@@ -122,9 +119,8 @@ class LogColors:
122
119
  return f"\033[38;5;{code}m"
123
120
 
124
121
  @staticmethod
125
- def combine(*styles: str) -> str:
126
- """Combine multiple color and style codes."""
127
- return "".join(styles)
122
+ def combine(*colors):
123
+ return "".join(colors)
128
124
 
129
125
  @staticmethod
130
126
  def gradient(text: str, start_rgb: tuple, end_rgb: tuple) -> str:
@@ -173,6 +169,28 @@ class LogColors:
173
169
  }
174
170
  return effects.get(effect, LogColors.BLINK) + text + LogColors.RESET
175
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
+
176
194
 
177
195
  class LogTheme:
178
196
  """Pre-defined color themes and combinations."""
@@ -228,4 +246,4 @@ LogColors.LEVEL_COLORS = {
228
246
  "WARNING": LogTheme.get_theme("warning"),
229
247
  "ERROR": LogTheme.get_theme("error"),
230
248
  "CRITICAL": LogTheme.get_theme("critical")
231
- }
249
+ }