adamops 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. adamops/__init__.py +40 -0
  2. adamops/cli.py +163 -0
  3. adamops/data/__init__.py +24 -0
  4. adamops/data/feature_engineering.py +284 -0
  5. adamops/data/loaders.py +922 -0
  6. adamops/data/preprocessors.py +227 -0
  7. adamops/data/splitters.py +218 -0
  8. adamops/data/validators.py +148 -0
  9. adamops/deployment/__init__.py +21 -0
  10. adamops/deployment/api.py +237 -0
  11. adamops/deployment/cloud.py +191 -0
  12. adamops/deployment/containerize.py +262 -0
  13. adamops/deployment/exporters.py +148 -0
  14. adamops/evaluation/__init__.py +24 -0
  15. adamops/evaluation/comparison.py +133 -0
  16. adamops/evaluation/explainability.py +143 -0
  17. adamops/evaluation/metrics.py +233 -0
  18. adamops/evaluation/reports.py +165 -0
  19. adamops/evaluation/visualization.py +238 -0
  20. adamops/models/__init__.py +21 -0
  21. adamops/models/automl.py +277 -0
  22. adamops/models/ensembles.py +228 -0
  23. adamops/models/modelops.py +308 -0
  24. adamops/models/registry.py +250 -0
  25. adamops/monitoring/__init__.py +21 -0
  26. adamops/monitoring/alerts.py +200 -0
  27. adamops/monitoring/dashboard.py +117 -0
  28. adamops/monitoring/drift.py +212 -0
  29. adamops/monitoring/performance.py +195 -0
  30. adamops/pipelines/__init__.py +15 -0
  31. adamops/pipelines/orchestrators.py +183 -0
  32. adamops/pipelines/workflows.py +212 -0
  33. adamops/utils/__init__.py +18 -0
  34. adamops/utils/config.py +457 -0
  35. adamops/utils/helpers.py +663 -0
  36. adamops/utils/logging.py +412 -0
  37. adamops-0.1.0.dist-info/METADATA +310 -0
  38. adamops-0.1.0.dist-info/RECORD +42 -0
  39. adamops-0.1.0.dist-info/WHEEL +5 -0
  40. adamops-0.1.0.dist-info/entry_points.txt +2 -0
  41. adamops-0.1.0.dist-info/licenses/LICENSE +21 -0
  42. adamops-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,412 @@
1
+ """
2
+ AdamOps Logging Module
3
+
4
+ Provides centralized logging functionality for the entire library.
5
+ Supports console and file logging with configurable levels and formats.
6
+ """
7
+
8
+ import logging
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Optional, Union
12
+ from logging.handlers import RotatingFileHandler
13
+ from datetime import datetime
14
+
15
+ # Custom log levels
16
+ TRACE = 5
17
+ logging.addLevelName(TRACE, "TRACE")
18
+
19
+ # Module-level logger cache
20
+ _loggers: dict = {}
21
+
22
+ # Default format
23
+ DEFAULT_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
24
+ DEFAULT_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
25
+
26
+
27
+ class ColoredFormatter(logging.Formatter):
28
+ """
29
+ Colored formatter for console output.
30
+
31
+ Adds color codes to log messages based on log level.
32
+ """
33
+
34
+ # ANSI color codes
35
+ COLORS = {
36
+ "TRACE": "\033[37m", # White
37
+ "DEBUG": "\033[36m", # Cyan
38
+ "INFO": "\033[32m", # Green
39
+ "WARNING": "\033[33m", # Yellow
40
+ "ERROR": "\033[31m", # Red
41
+ "CRITICAL": "\033[35m", # Magenta
42
+ }
43
+ RESET = "\033[0m"
44
+ BOLD = "\033[1m"
45
+
46
+ def __init__(self, fmt: Optional[str] = None, datefmt: Optional[str] = None, use_colors: bool = True):
47
+ super().__init__(fmt or DEFAULT_FORMAT, datefmt or DEFAULT_DATE_FORMAT)
48
+ self.use_colors = use_colors
49
+
50
+ def format(self, record: logging.LogRecord) -> str:
51
+ """Format log record with optional colors."""
52
+ # Save original levelname
53
+ original_levelname = record.levelname
54
+
55
+ if self.use_colors and record.levelname in self.COLORS:
56
+ color = self.COLORS[record.levelname]
57
+ record.levelname = f"{color}{self.BOLD}{record.levelname}{self.RESET}"
58
+ record.msg = f"{color}{record.msg}{self.RESET}"
59
+
60
+ result = super().format(record)
61
+
62
+ # Restore original levelname
63
+ record.levelname = original_levelname
64
+
65
+ return result
66
+
67
+
68
+ class AdamOpsLogger:
69
+ """
70
+ Custom logger class for AdamOps.
71
+
72
+ Provides a unified logging interface with features like:
73
+ - Console and file logging
74
+ - Colored output
75
+ - Automatic log rotation
76
+ - Context managers for temporary log level changes
77
+
78
+ Example:
79
+ >>> logger = AdamOpsLogger("my_module")
80
+ >>> logger.info("This is an info message")
81
+ >>> logger.debug("This is a debug message")
82
+ """
83
+
84
+ def __init__(
85
+ self,
86
+ name: str,
87
+ level: str = "INFO",
88
+ log_file: Optional[str] = None,
89
+ console: bool = True,
90
+ use_colors: bool = True,
91
+ format_string: Optional[str] = None,
92
+ max_bytes: int = 10485760, # 10MB
93
+ backup_count: int = 5,
94
+ ):
95
+ """
96
+ Initialize the logger.
97
+
98
+ Args:
99
+ name: Logger name (usually module name).
100
+ level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
101
+ log_file: Optional path to log file.
102
+ console: Whether to log to console.
103
+ use_colors: Whether to use colored output.
104
+ format_string: Custom format string.
105
+ max_bytes: Maximum log file size before rotation.
106
+ backup_count: Number of backup files to keep.
107
+ """
108
+ self.name = name
109
+ self._logger = logging.getLogger(name)
110
+ self._logger.setLevel(getattr(logging, level.upper(), logging.INFO))
111
+ self._logger.handlers = [] # Clear existing handlers
112
+
113
+ format_str = format_string or DEFAULT_FORMAT
114
+
115
+ # Console handler
116
+ if console:
117
+ console_handler = logging.StreamHandler(sys.stdout)
118
+ console_handler.setFormatter(ColoredFormatter(format_str, use_colors=use_colors))
119
+ self._logger.addHandler(console_handler)
120
+
121
+ # File handler
122
+ if log_file:
123
+ log_path = Path(log_file)
124
+ log_path.parent.mkdir(parents=True, exist_ok=True)
125
+
126
+ file_handler = RotatingFileHandler(
127
+ log_file,
128
+ maxBytes=max_bytes,
129
+ backupCount=backup_count,
130
+ encoding="utf-8",
131
+ )
132
+ file_handler.setFormatter(logging.Formatter(format_str, DEFAULT_DATE_FORMAT))
133
+ self._logger.addHandler(file_handler)
134
+
135
+ # Prevent propagation to root logger
136
+ self._logger.propagate = False
137
+
138
+ def set_level(self, level: str) -> None:
139
+ """Set the log level."""
140
+ self._logger.setLevel(getattr(logging, level.upper(), logging.INFO))
141
+
142
+ def trace(self, msg: str, *args, **kwargs) -> None:
143
+ """Log a TRACE level message."""
144
+ self._logger.log(TRACE, msg, *args, **kwargs)
145
+
146
+ def debug(self, msg: str, *args, **kwargs) -> None:
147
+ """Log a DEBUG level message."""
148
+ self._logger.debug(msg, *args, **kwargs)
149
+
150
+ def info(self, msg: str, *args, **kwargs) -> None:
151
+ """Log an INFO level message."""
152
+ self._logger.info(msg, *args, **kwargs)
153
+
154
+ def warning(self, msg: str, *args, **kwargs) -> None:
155
+ """Log a WARNING level message."""
156
+ self._logger.warning(msg, *args, **kwargs)
157
+
158
+ def warn(self, msg: str, *args, **kwargs) -> None:
159
+ """Alias for warning."""
160
+ self.warning(msg, *args, **kwargs)
161
+
162
+ def error(self, msg: str, *args, **kwargs) -> None:
163
+ """Log an ERROR level message."""
164
+ self._logger.error(msg, *args, **kwargs)
165
+
166
+ def critical(self, msg: str, *args, **kwargs) -> None:
167
+ """Log a CRITICAL level message."""
168
+ self._logger.critical(msg, *args, **kwargs)
169
+
170
+ def exception(self, msg: str, *args, **kwargs) -> None:
171
+ """Log an exception with traceback."""
172
+ self._logger.exception(msg, *args, **kwargs)
173
+
174
+ def log(self, level: int, msg: str, *args, **kwargs) -> None:
175
+ """Log a message at the specified level."""
176
+ self._logger.log(level, msg, *args, **kwargs)
177
+
178
+
179
+ def get_logger(
180
+ name: str = "adamops",
181
+ level: Optional[str] = None,
182
+ log_file: Optional[str] = None,
183
+ console: bool = True,
184
+ use_colors: bool = True,
185
+ ) -> AdamOpsLogger:
186
+ """
187
+ Get or create a logger with the specified name.
188
+
189
+ Args:
190
+ name: Logger name.
191
+ level: Log level (default: INFO).
192
+ log_file: Optional path to log file.
193
+ console: Whether to log to console.
194
+ use_colors: Whether to use colored output.
195
+
196
+ Returns:
197
+ AdamOpsLogger: Logger instance.
198
+
199
+ Example:
200
+ >>> logger = get_logger("my_module")
201
+ >>> logger.info("Processing data...")
202
+ """
203
+ # Use default level from config if not specified
204
+ if level is None:
205
+ try:
206
+ from adamops.utils.config import get_config
207
+ config = get_config()
208
+ level = config.logging.level
209
+ if log_file is None:
210
+ log_file = config.logging.file
211
+ except ImportError:
212
+ level = "INFO"
213
+
214
+ # Check cache
215
+ cache_key = f"{name}:{level}:{log_file}:{console}:{use_colors}"
216
+ if cache_key not in _loggers:
217
+ _loggers[cache_key] = AdamOpsLogger(
218
+ name=name,
219
+ level=level,
220
+ log_file=log_file,
221
+ console=console,
222
+ use_colors=use_colors,
223
+ )
224
+
225
+ return _loggers[cache_key]
226
+
227
+
228
+ def setup_logging(
229
+ level: str = "INFO",
230
+ log_file: Optional[str] = None,
231
+ console: bool = True,
232
+ use_colors: bool = True,
233
+ format_string: Optional[str] = None,
234
+ ) -> None:
235
+ """
236
+ Set up global logging configuration for AdamOps.
237
+
238
+ Args:
239
+ level: Global log level.
240
+ log_file: Path to log file.
241
+ console: Whether to log to console.
242
+ use_colors: Whether to use colored output.
243
+ format_string: Custom format string.
244
+
245
+ Example:
246
+ >>> setup_logging(level="DEBUG", log_file="adamops.log")
247
+ """
248
+ # Configure root adamops logger
249
+ root_logger = logging.getLogger("adamops")
250
+ root_logger.setLevel(getattr(logging, level.upper(), logging.INFO))
251
+ root_logger.handlers = []
252
+
253
+ format_str = format_string or DEFAULT_FORMAT
254
+
255
+ if console:
256
+ console_handler = logging.StreamHandler(sys.stdout)
257
+ console_handler.setFormatter(ColoredFormatter(format_str, use_colors=use_colors))
258
+ root_logger.addHandler(console_handler)
259
+
260
+ if log_file:
261
+ log_path = Path(log_file)
262
+ log_path.parent.mkdir(parents=True, exist_ok=True)
263
+
264
+ file_handler = RotatingFileHandler(
265
+ log_file,
266
+ maxBytes=10485760,
267
+ backupCount=5,
268
+ encoding="utf-8",
269
+ )
270
+ file_handler.setFormatter(logging.Formatter(format_str, DEFAULT_DATE_FORMAT))
271
+ root_logger.addHandler(file_handler)
272
+
273
+
274
+ class LogContext:
275
+ """
276
+ Context manager for temporary log level changes.
277
+
278
+ Example:
279
+ >>> logger = get_logger("my_module")
280
+ >>> with LogContext(logger, "DEBUG"):
281
+ ... logger.debug("This will be logged")
282
+ >>> logger.debug("This might not be logged")
283
+ """
284
+
285
+ def __init__(self, logger: Union[AdamOpsLogger, logging.Logger], level: str):
286
+ """
287
+ Initialize context manager.
288
+
289
+ Args:
290
+ logger: Logger to modify.
291
+ level: Temporary log level.
292
+ """
293
+ self.logger = logger
294
+ self.new_level = getattr(logging, level.upper(), logging.INFO)
295
+ self.old_level = None
296
+
297
+ def __enter__(self) -> None:
298
+ """Enter context and set new level."""
299
+ if isinstance(self.logger, AdamOpsLogger):
300
+ self.old_level = self.logger._logger.level
301
+ self.logger._logger.setLevel(self.new_level)
302
+ else:
303
+ self.old_level = self.logger.level
304
+ self.logger.setLevel(self.new_level)
305
+
306
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
307
+ """Exit context and restore old level."""
308
+ if isinstance(self.logger, AdamOpsLogger):
309
+ self.logger._logger.setLevel(self.old_level)
310
+ else:
311
+ self.logger.setLevel(self.old_level)
312
+
313
+
314
+ class Timer:
315
+ """
316
+ Context manager for timing operations with optional logging.
317
+
318
+ Example:
319
+ >>> logger = get_logger("my_module")
320
+ >>> with Timer("Data loading", logger):
321
+ ... load_data()
322
+ [INFO] Data loading completed in 2.34s
323
+ """
324
+
325
+ def __init__(self, operation: str, logger: Optional[AdamOpsLogger] = None, level: str = "INFO"):
326
+ """
327
+ Initialize timer.
328
+
329
+ Args:
330
+ operation: Name of the operation being timed.
331
+ logger: Optional logger for timing output.
332
+ level: Log level for timing message.
333
+ """
334
+ self.operation = operation
335
+ self.logger = logger
336
+ self.level = level.upper()
337
+ self.start_time: Optional[datetime] = None
338
+ self.end_time: Optional[datetime] = None
339
+
340
+ @property
341
+ def elapsed(self) -> float:
342
+ """Get elapsed time in seconds."""
343
+ if self.start_time is None:
344
+ return 0.0
345
+ end = self.end_time or datetime.now()
346
+ return (end - self.start_time).total_seconds()
347
+
348
+ def __enter__(self) -> "Timer":
349
+ """Start timer."""
350
+ self.start_time = datetime.now()
351
+ if self.logger:
352
+ self.logger.log(
353
+ getattr(logging, self.level, logging.INFO),
354
+ f"Starting: {self.operation}..."
355
+ )
356
+ return self
357
+
358
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
359
+ """Stop timer and log result."""
360
+ self.end_time = datetime.now()
361
+ if self.logger:
362
+ status = "completed" if exc_type is None else "failed"
363
+ self.logger.log(
364
+ getattr(logging, self.level, logging.INFO),
365
+ f"{self.operation} {status} in {self.elapsed:.2f}s"
366
+ )
367
+
368
+
369
+ def log_function_call(logger: Optional[AdamOpsLogger] = None, level: str = "DEBUG"):
370
+ """
371
+ Decorator to log function calls and their results.
372
+
373
+ Args:
374
+ logger: Logger to use (creates one if not provided).
375
+ level: Log level for function call messages.
376
+
377
+ Example:
378
+ >>> @log_function_call()
379
+ ... def process_data(df):
380
+ ... return df.dropna()
381
+ """
382
+ def decorator(func):
383
+ def wrapper(*args, **kwargs):
384
+ nonlocal logger
385
+ if logger is None:
386
+ logger = get_logger(func.__module__)
387
+
388
+ log_level = getattr(logging, level.upper(), logging.DEBUG)
389
+
390
+ # Log function call
391
+ args_str = ", ".join([repr(a) for a in args[:3]]) # Limit args
392
+ if len(args) > 3:
393
+ args_str += ", ..."
394
+ kwargs_str = ", ".join([f"{k}={repr(v)}" for k, v in list(kwargs.items())[:3]])
395
+ if len(kwargs) > 3:
396
+ kwargs_str += ", ..."
397
+
398
+ call_str = f"{func.__name__}({args_str}{', ' if args_str and kwargs_str else ''}{kwargs_str})"
399
+ logger.log(log_level, f"Calling: {call_str}")
400
+
401
+ try:
402
+ result = func(*args, **kwargs)
403
+ logger.log(log_level, f"{func.__name__} returned successfully")
404
+ return result
405
+ except Exception as e:
406
+ logger.error(f"{func.__name__} raised {type(e).__name__}: {e}")
407
+ raise
408
+
409
+ wrapper.__name__ = func.__name__
410
+ wrapper.__doc__ = func.__doc__
411
+ return wrapper
412
+ return decorator