devdox-ai-locust 0.1.1__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 devdox-ai-locust might be problematic. Click here for more details.

File without changes
File without changes
@@ -0,0 +1,24 @@
1
+ from pydantic import BaseModel, field_validator, ValidationInfo
2
+ from typing import Optional, Type
3
+
4
+
5
+ class SwaggerProcessingRequest(BaseModel):
6
+ swagger_url: Optional[str] = None
7
+
8
+ @field_validator("swagger_url", mode="before")
9
+ @classmethod
10
+ def coerce_to_string(
11
+ cls: Type["SwaggerProcessingRequest"], v: Optional[str]
12
+ ) -> Optional[str]:
13
+ if v is None:
14
+ return v
15
+ return str(v)
16
+
17
+ @field_validator("swagger_url")
18
+ @classmethod
19
+ def validate_url_when_source_is_url(
20
+ cls: Type["SwaggerProcessingRequest"], v: Optional[str], info: ValidationInfo
21
+ ) -> Optional[str]:
22
+ if info.data.get("swagger_source") == "url" and not v:
23
+ raise ValueError("swagger_url is required when source is url")
24
+ return v
@@ -0,0 +1,180 @@
1
+ """
2
+ Locust performance tests for {{api_info.title}}
3
+
4
+ API Information:
5
+ - Title: {{api_info.title}}
6
+ - Version: {{api_info.version}}
7
+ - Base URL: {{api_info.base_url}}
8
+ """
9
+
10
+ from locust import HttpUser, task, between, events, SequentialTaskSet
11
+ import json
12
+ import logging
13
+ from typing import Dict, Any, Optional
14
+ from urllib.parse import urljoin
15
+
16
+ from test_data import TestDataGenerator
17
+ from utils import ResponseValidator, RequestLogger, PerformanceMonitor
18
+ from config import LoadTestConfig
19
+
20
+ # Configure logging
21
+ logging.basicConfig(level=logging.INFO)
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Initialize components
25
+ config = LoadTestConfig()
26
+ data_generator = TestDataGenerator()
27
+ response_validator = ResponseValidator()
28
+ performance_monitor = PerformanceMonitor()
29
+
30
+
31
+ class BaseTaskMethods:
32
+ """Mixin class with common task functionality - no inheritance conflicts"""
33
+
34
+ def __init__(self, *args, **kwargs):
35
+ super().__init__(*args, **kwargs)
36
+ self._initialize_attributes()
37
+
38
+ def _initialize_attributes(self):
39
+ """Initialize all required attributes"""
40
+ if not hasattr(self, 'auth_token'):
41
+ self.auth_token = None
42
+ if not hasattr(self, 'user_data'):
43
+ self.user_data = {}
44
+ if not hasattr(self, 'request_count'):
45
+ self.request_count = 0
46
+ if not hasattr(self, 'default_headers'):
47
+ self._setup_authentication()
48
+ self._setup_headers()
49
+
50
+ def on_start(self):
51
+ """Initialize user session"""
52
+ self._initialize_attributes()
53
+ logger.info(f"TaskSet {self.__class__.__name__} started")
54
+
55
+ def on_stop(self):
56
+ """Cleanup when user stops"""
57
+ if hasattr(self, 'request_count'):
58
+ logger.info(f"TaskSet {self.__class__.__name__} stopped after {self.request_count} requests")
59
+ else:
60
+ logger.info(f"TaskSet {self.__class__.__name__} stopped after {self.request_count} requests")
61
+
62
+ def _setup_authentication(self):
63
+ """Setup authentication (override in subclasses if needed)"""
64
+ if not hasattr(self, 'auth_token'):
65
+ self.auth_token = None
66
+ if config.api_key:
67
+ self.auth_token = config.api_key
68
+
69
+ def _setup_headers(self):
70
+ """Setup default headers for all requests"""
71
+ if not hasattr(self, 'auth_token'):
72
+ self._setup_authentication()
73
+ self.default_headers = {
74
+ "Content-Type": "application/json",
75
+ "Accept": "application/json",
76
+ "User-Agent": "Locust-LoadTest/1.0"
77
+ }
78
+ if self.auth_token:
79
+ self.default_headers["Authorization"] = f"Bearer {self.auth_token}"
80
+
81
+ def make_request(self, method: str, path: str, **kwargs) -> Optional[Dict]:
82
+ """Make HTTP request with logging, validation, and monitoring"""
83
+ self._initialize_attributes()
84
+ self.request_count += 1
85
+
86
+ try:
87
+ # Merge headers
88
+ request_headers = {**self.default_headers, **kwargs.get("headers", {})}
89
+
90
+ # Set content-type for POST/PUT/PATCH
91
+ if method.upper() in ["POST", "PUT", "PATCH"]:
92
+ if "json" in kwargs:
93
+ request_headers["Content-Type"] = "application/json"
94
+ elif "data" in kwargs and isinstance(kwargs["data"], dict):
95
+ request_headers["Content-Type"] = "application/x-www-form-urlencoded"
96
+
97
+ kwargs["headers"] = request_headers
98
+
99
+ # Log request
100
+ RequestLogger.log_request(method, path, kwargs)
101
+
102
+ with self.client.request(
103
+ method=method,
104
+ url=urljoin(config.base_url, path),
105
+ catch_response=True,
106
+ **kwargs
107
+ ) as response:
108
+ # Validate and monitor
109
+ is_valid = response_validator.validate_response(response, method, path)
110
+ performance_monitor.record_response(response, method, path)
111
+
112
+ if not is_valid:
113
+ response.failure(f"Response validation failed for {method} {path}")
114
+ return None
115
+
116
+ try:
117
+ return response.json() if response.content else None
118
+ except json.JSONDecodeError:
119
+ if response.status_code < 400:
120
+ return {"raw_content": response.text}
121
+ response.failure(f"Invalid JSON response for {method} {path}")
122
+ return None
123
+
124
+ except Exception as e:
125
+ logger.error(f"Request failed {method} {path}: {e}")
126
+ return None
127
+
128
+ def _store_response_data(self, method_name: str, data: Dict):
129
+ """Store response data for future requests"""
130
+ if not hasattr(self, 'user_data'):
131
+ self.user_data = {}
132
+ if data:
133
+ self.user_data[method_name] = data
134
+
135
+ def _get_stored_data(self, method_name: str, key: str = None):
136
+ """Retrieve stored response data from previous requests"""
137
+ stored_data = self.user_data.get(method_name)
138
+ if stored_data and key:
139
+ return stored_data.get(key)
140
+ return stored_data
141
+
142
+
143
+ class BaseAPIUser(HttpUser):
144
+ """Base class for API users with common functionality"""
145
+
146
+ abstract = True
147
+ wait_time = between(0.5, 1.5)
148
+
149
+ def on_start(self):
150
+ """Initialize user session"""
151
+ self.auth_token = None
152
+ self.user_data = {}
153
+ self.request_count = 0
154
+
155
+ # Setup authentication if needed
156
+ self._setup_authentication()
157
+
158
+ # Initialize session headers
159
+ self._setup_headers()
160
+
161
+ logger.info(f"User {self.__class__.__name__} started")
162
+
163
+ def on_stop(self):
164
+ """Cleanup when user stops"""
165
+ logger.info(f"User {self.__class__.__name__} stopped after {self.request_count} requests")
166
+
167
+ def _setup_authentication(self):
168
+ """Setup authentication (override in subclasses if needed)"""
169
+ if config.api_key:
170
+ self.auth_token = config.api_key
171
+
172
+ def _setup_headers(self):
173
+ """Setup default headers for all requests"""
174
+ self.default_headers = {
175
+ "Content-Type": "application/json",
176
+ "Accept": "application/json",
177
+ "User-Agent": "Locust-LoadTest/1.0"
178
+ }
179
+ if self.auth_token:
180
+ self.default_headers["Authorization"] = f"Bearer {self.auth_token}"
@@ -0,0 +1,173 @@
1
+ """
2
+ Configuration for Locust Load Tests
3
+ """
4
+
5
+ import os
6
+ from dataclasses import dataclass
7
+ from typing import Dict, List, Optional, Any
8
+ from urllib.parse import urljoin
9
+ from dotenv import load_dotenv
10
+
11
+ load_dotenv()
12
+
13
+
14
+ @dataclass
15
+ class PerformanceThresholds:
16
+ """Performance thresholds for test validation"""
17
+ max_response_time_ms: int = 2000 # Maximum acceptable response time
18
+ max_95th_percentile_ms: int = 5000 # 95th percentile response time
19
+ max_error_rate_percent: float = 1.0 # Maximum error rate percentage
20
+ min_requests_per_second: float = 10.0 # Minimum RPS threshold
21
+
22
+
23
+ @dataclass
24
+ class LoadTestScenario:
25
+ """Load test scenario configuration"""
26
+ name: str
27
+ users: int
28
+ spawn_rate: int
29
+ run_time: str
30
+ description: str
31
+
32
+
33
+ class LoadTestConfig:
34
+ """Main configuration class for load tests"""
35
+
36
+ def __init__(self):
37
+ # API Configuration
38
+ self.base_url = os.getenv('API_BASE_URL', '{{api_info.base_url}}')
39
+ self.api_version = os.getenv('API_VERSION', '{{api_info.version}}')
40
+ self.api_title = '{{api_info.title}}'
41
+
42
+ # Authentication
43
+ self.api_key = os.getenv('API_KEY')
44
+ self.auth_token = os.getenv('AUTH_TOKEN')
45
+ self.username = os.getenv('API_USERNAME')
46
+ self.password = os.getenv('API_PASSWORD')
47
+
48
+ # Load Test Parameters
49
+ self.users = int(os.getenv('LOCUST_USERS', '10'))
50
+ self.spawn_rate = int(os.getenv('LOCUST_SPAWN_RATE', '2'))
51
+ self.run_time = os.getenv('LOCUST_RUN_TIME', '5m')
52
+ self.host = os.getenv('LOCUST_HOST', self.base_url)
53
+
54
+ # Test Data Configuration
55
+ self.use_realistic_data = os.getenv('USE_REALISTIC_DATA', 'true').lower() == 'true'
56
+ self.data_seed = int(os.getenv('DATA_SEED', '42'))
57
+
58
+ # Monitoring and Reporting
59
+ self.enable_monitoring = os.getenv('ENABLE_MONITORING', 'true').lower() == 'true'
60
+ self.report_output_dir = os.getenv('REPORT_OUTPUT_DIR', './reports')
61
+ self.log_level = os.getenv('LOG_LEVEL', 'INFO')
62
+
63
+ # Performance Thresholds
64
+ self.thresholds = PerformanceThresholds(
65
+ max_response_time_ms=int(os.getenv('MAX_RESPONSE_TIME_MS', '2000')),
66
+ max_95th_percentile_ms=int(os.getenv('MAX_95TH_PERCENTILE_MS', '5000')),
67
+ max_error_rate_percent=float(os.getenv('MAX_ERROR_RATE_PERCENT', '1.0')),
68
+ min_requests_per_second=float(os.getenv('MIN_REQUESTS_PER_SECOND', '10.0'))
69
+ )
70
+
71
+ # Test Scenarios
72
+ self.scenarios = self._load_test_scenarios()
73
+
74
+ # Request Configuration
75
+ self.request_timeout = int(os.getenv('REQUEST_TIMEOUT', '30'))
76
+ self.max_retries = int(os.getenv('MAX_RETRIES', '3'))
77
+
78
+ # Load Balancing and Distribution
79
+ self.enable_distributed = os.getenv('ENABLE_DISTRIBUTED', 'false').lower() == 'true'
80
+ self.master_host = os.getenv('LOCUST_MASTER_HOST', 'localhost')
81
+ self.master_port = int(os.getenv('LOCUST_MASTER_PORT', '5557'))
82
+
83
+ # Feature Flags
84
+ self.enable_custom_flows = os.getenv('ENABLE_CUSTOM_FLOWS', 'true').lower() == 'true'
85
+ self.enable_response_validation = os.getenv('ENABLE_RESPONSE_VALIDATION', 'true').lower() == 'true'
86
+ self.enable_performance_monitoring = os.getenv('ENABLE_PERF_MONITORING', 'true').lower() == 'true'
87
+
88
+ def _load_test_scenarios(self) -> Dict[str, LoadTestScenario]:
89
+ """Load predefined test scenarios"""
90
+ return {
91
+ 'smoke': LoadTestScenario(
92
+ name='Smoke Test',
93
+ users=5,
94
+ spawn_rate=1,
95
+ run_time='2m',
96
+ description='Quick smoke test to verify basic functionality'
97
+ ),
98
+ 'load': LoadTestScenario(
99
+ name='Load Test',
100
+ users=50,
101
+ spawn_rate=5,
102
+ run_time='10m',
103
+ description='Standard load test with moderate user count'
104
+ ),
105
+ 'stress': LoadTestScenario(
106
+ name='Stress Test',
107
+ users=200,
108
+ spawn_rate=10,
109
+ run_time='15m',
110
+ description='Stress test to find breaking points'
111
+ ),
112
+ 'spike': LoadTestScenario(
113
+ name='Spike Test',
114
+ users=500,
115
+ spawn_rate=50,
116
+ run_time='5m',
117
+ description='Spike test with rapid user ramp-up'
118
+ ),
119
+ 'soak': LoadTestScenario(
120
+ name='Soak Test',
121
+ users=100,
122
+ spawn_rate=5,
123
+ run_time='60m',
124
+ description='Long-running test to identify memory leaks'
125
+ )
126
+ }
127
+
128
+ def get_scenario(self, scenario_name: str) -> Optional[LoadTestScenario]:
129
+ """Get specific test scenario"""
130
+ return self.scenarios.get(scenario_name)
131
+
132
+ def get_headers(self) -> Dict[str, str]:
133
+ """Get default headers for requests"""
134
+ headers = {
135
+ 'Content-Type': 'application/json',
136
+ 'Accept': 'application/json',
137
+ 'User-Agent': 'LoadTest/1.0'
138
+ }
139
+
140
+ if self.api_key:
141
+ headers['X-API-Key'] = self.api_key
142
+
143
+ if self.auth_token:
144
+ headers['Authorization'] = f'Bearer {self.auth_token}'
145
+
146
+ return headers
147
+
148
+ def get_full_url(self, path: str) -> str:
149
+ """Construct full URL from path"""
150
+ return urljoin(self.base_url, path.lstrip('/'))
151
+
152
+ def validate_config(self) -> List[str]:
153
+ """Validate configuration and return any errors"""
154
+ errors = []
155
+
156
+ if not self.base_url:
157
+ errors.append("Base URL is required")
158
+
159
+ if self.users <= 0:
160
+ errors.append("User count must be positive")
161
+
162
+ if self.spawn_rate <= 0:
163
+ errors.append("Spawn rate must be positive")
164
+
165
+ if self.spawn_rate > self.users:
166
+ errors.append("Spawn rate cannot exceed user count")
167
+
168
+ return errors
169
+
170
+
171
+ # Global configuration instance
172
+ config = LoadTestConfig()
173
+
@@ -0,0 +1,95 @@
1
+ import random
2
+ import time
3
+ from typing import Dict, List, Any, Optional
4
+ from locust import HttpUser, task, between, SequentialTaskSet
5
+ import logging
6
+
7
+ from test_data import TestDataGenerator
8
+ from utils import ResponseValidator, data_manager
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class APIWorkflowUser(HttpUser):
14
+ """User that executes complex workflows"""
15
+
16
+ wait_time = between(2, 8)
17
+ weight = 2
18
+
19
+ tasks = []
20
+
21
+ def on_start(self):
22
+ self.workflow_data = {}
23
+ logger.info("Workflow user started")
24
+
25
+ def on_stop(self):
26
+ logger.info("Workflow user stopped")
27
+
28
+
29
+ class DataDependentFlow(SequentialTaskSet):
30
+ """Flow that demonstrates data dependencies between requests"""
31
+
32
+ @task
33
+ def create_resource(self):
34
+ resource_data = {
35
+ 'name': f"resource_{random.randint(1000, 9999)}",
36
+ 'type': random.choice(['document', 'image', 'video']),
37
+ 'metadata': {
38
+ 'created_by': 'load_test',
39
+ 'test_run': True
40
+ }
41
+ }
42
+
43
+ response_data = self.user.make_request(
44
+ method="post",
45
+ url="/resources",
46
+ json=resource_data
47
+ )
48
+
49
+ if response_data and 'id' in response_data:
50
+ data_manager.store_shared_data('last_resource_id', response_data['id'])
51
+ self.user.user_data['resource_id'] = response_data['id']
52
+
53
+ @task
54
+ def update_resource(self):
55
+ resource_id = self.user.user_data.get('resource_id') or data_manager.get_shared_data('last_resource_id')
56
+ if resource_id:
57
+ update_data = {
58
+ 'name': f"updated_resource_{random.randint(1000, 9999)}",
59
+ 'status': 'active'
60
+ }
61
+ self.user.make_request(
62
+ method="put",
63
+ url=f"/resources/{resource_id}",
64
+ json=update_data
65
+ )
66
+
67
+ @task
68
+ def get_resource(self):
69
+ resource_id = self.user.user_data.get('resource_id')
70
+ if resource_id:
71
+ self.user.make_request(
72
+ method="get",
73
+ url=f"/resources/{resource_id}"
74
+ )
75
+
76
+ @task
77
+ def delete_resource(self):
78
+ resource_id = self.user.user_data.get('resource_id')
79
+ if resource_id:
80
+ self.user.make_request(
81
+ method="delete",
82
+ url=f"/resources/{resource_id}"
83
+ )
84
+ self.user.user_data.pop('resource_id', None)
85
+
86
+
87
+ class ComplexFlowUser(HttpUser):
88
+ """User that executes complex, realistic flows"""
89
+
90
+ wait_time = between(3, 10)
91
+ weight = 1
92
+
93
+ tasks = [
94
+ DataDependentFlow
95
+ ]
@@ -0,0 +1,34 @@
1
+ """
2
+ Locust performance tests for {{group}}
3
+ Generated on: {{ generated_at }}
4
+
5
+ API Information:
6
+ - Title: {{ api_info.title }}
7
+ - Version: {{ api_info.version }}
8
+ - Base URL: {{ api_info.base_url }}
9
+ """
10
+
11
+ from locust import HttpUser, task, between, events, SequentialTaskSet
12
+ from locust.runners import MasterRunner
13
+ import logging
14
+
15
+ from test_data import TestDataGenerator
16
+ from utils import ResponseValidator, RequestLogger, PerformanceMonitor
17
+ from workflows.base_workflow import BaseAPIUser, BaseTaskMethods
18
+ from config import LoadTestConfig
19
+
20
+ # Configure logging
21
+ logging.basicConfig(level=logging.INFO)
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Initialize components
25
+ config = LoadTestConfig()
26
+ data_generator = TestDataGenerator()
27
+ response_validator = ResponseValidator()
28
+ performance_monitor = PerformanceMonitor()
29
+
30
+
31
+ class {{group}}TaskMethods(SequentialTaskSet, BaseTaskMethods):
32
+ """Mixin class containing all API task methods"""
33
+
34
+ {{task_methods_content}}
@@ -0,0 +1,3 @@
1
+ {% for key, value in environment_vars.items() -%}
2
+ {{ key }}={{ value }}
3
+ {% endfor %}
@@ -0,0 +1,25 @@
1
+ """
2
+ Fallback Locust test file for {api_info.get('title', 'API')}
3
+ """
4
+
5
+ from locust import HttpUser, task, between
6
+ import logging
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class BasicAPIUser(HttpUser):
12
+ wait_time = between(1, 5)
13
+
14
+ @task(1)
15
+ def health_check(self):
16
+ """Basic health check"""
17
+ with self.client.get("/health", catch_response=True) as response:
18
+ if response.status_code == 200:
19
+ response.success()
20
+ else:
21
+ response.failure(f"Health check failed: {{response.status_code}}")
22
+
23
+
24
+ if __name__ == "__main__":
25
+ print("Running fallback Locust test for {{api_info.title}}")
@@ -0,0 +1,70 @@
1
+ """
2
+ Locust performance tests for {{ api_info.get('title', 'API') }}
3
+ Generated on: {{ generated_at }}
4
+
5
+ API Information:
6
+ - Title: {{ api_info.get('title', 'Unknown') }}
7
+ - Version: {{ api_info.get('version', 'Unknown') }}
8
+ - Base URL: {{ api_info.get('base_url', 'http://localhost') }}
9
+ """
10
+
11
+ from locust import HttpUser, task, between, events
12
+ from locust.runners import MasterRunner
13
+ import json
14
+ import random
15
+ import logging
16
+ from typing import Dict, Any, Optional
17
+ from urllib.parse import urljoin
18
+ from workflows.base_workflow import BaseAPIUser
19
+ {{ import_group_tasks }}
20
+
21
+ from test_data import TestDataGenerator
22
+ from utils import ResponseValidator, RequestLogger, PerformanceMonitor
23
+ from config import LoadTestConfig
24
+
25
+ # Configure logging
26
+ logging.basicConfig(level=logging.INFO)
27
+ logger = logging.getLogger(__name__)
28
+
29
+ # Initialize components
30
+ config = LoadTestConfig()
31
+ data_generator = TestDataGenerator()
32
+ response_validator = ResponseValidator()
33
+ performance_monitor = PerformanceMonitor()
34
+
35
+
36
+ class APITaskMethods:
37
+ """Mixin class containing all API task methods"""
38
+
39
+ {{ task_methods_content }}
40
+
41
+ tasks = {{ tasks_str }}
42
+
43
+
44
+ {{ generated_task_classes }}
45
+
46
+
47
+ # Event handlers
48
+ @events.request.add_listener
49
+ def on_request(request_type, name, response_time, response_length, exception, context, **kwargs):
50
+ performance_monitor.on_request_event(
51
+ request_type, name, response_time, response_length, exception, context
52
+ )
53
+
54
+
55
+ @events.test_start.add_listener
56
+ def on_test_start(environment, **kwargs):
57
+ logger.info("Load test starting...")
58
+ performance_monitor.test_start()
59
+
60
+
61
+ @events.test_stop.add_listener
62
+ def on_test_stop(environment, **kwargs):
63
+ logger.info("Load test stopping...")
64
+ performance_monitor.test_stop()
65
+ performance_monitor.generate_report()
66
+
67
+
68
+ if __name__ == "__main__":
69
+ from locust.main import main
70
+ main()
@@ -0,0 +1,46 @@
1
+ # 🚀 Load Testing Suite for {{ api_info.title }}
2
+
3
+ [![Python](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
4
+ [![Locust](https://img.shields.io/badge/locust-2.15+-green.svg)](https://locust.io/)
5
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+
7
+ Professional-grade performance testing suite for **{{ api_info.title }} {{ api_info.version }}** built with Locust.
8
+
9
+ {% if api_info.description -%}
10
+ {{ api_info.description }}
11
+ {% endif %}
12
+
13
+ ## 📋 Table of Contents
14
+
15
+ - [Quick Start](#-quick-start)
16
+ - [Test Scenarios](#-test-scenarios)
17
+ - [Configuration](#-configuration)
18
+ - [Running Tests](#-running-tests)
19
+ - [Monitoring & Reports](#-monitoring--reports)
20
+ - [Advanced Features](#-advanced-features)
21
+ - [Troubleshooting](#-troubleshooting)
22
+ - [Best Practices](#-best-practices)
23
+
24
+ ## 🚀 Quick Start
25
+
26
+ ### Prerequisites
27
+
28
+ - **Python 3.8+** with pip
29
+ - **Network access** to the target API
30
+ - **Minimum 2GB RAM** for load testing
31
+
32
+ ### Installation
33
+ ```bash
34
+ # 1. Clone or download the test suite
35
+ git clone <repository-url>
36
+ cd load-testing-suite
37
+
38
+ # 2. Install dependencies
39
+ pip install -r requirements.txt
40
+
41
+ # 3. Configure environment
42
+ cp .env.example .env
43
+ # Edit .env with your API configuration
44
+
45
+ # 4. Run your first test
46
+ locust -f locust.py
@@ -0,0 +1,31 @@
1
+ # Core Locust dependencies
2
+ locust>=2.15.0,<3.0.0
3
+
4
+ # HTTP and API testing
5
+ requests>=2.31.0
6
+ urllib3>=1.26.0
7
+
8
+ # Data generation and manipulation
9
+ python-dateutil>=2.8.0
10
+
11
+ # Data handling
12
+ pandas>=2.0.0
13
+ numpy>=1.24.0
14
+
15
+ # Configuration and environment
16
+ python-dotenv>=1.0.0
17
+ pydantic>=2.0.0
18
+
19
+ # Monitoring and reporting
20
+ psutil>=5.9.0
21
+ matplotlib>=3.7.0
22
+ seaborn>=0.12.0
23
+
24
+ # Logging and debugging
25
+ structlog>=23.0.0
26
+ colorama>=0.4.6
27
+ Faker==37.6.0
28
+
29
+ # Optional: Database connectivity
30
+ # psycopg2-binary>=2.9.0 # PostgreSQL
31
+ # pymongo>=4.3.0 # MongoDB