adaptive-executor 0.1.0__tar.gz
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.
- adaptive_executor-0.1.0/LICENSE +21 -0
- adaptive_executor-0.1.0/MANIFEST.in +6 -0
- adaptive_executor-0.1.0/PKG-INFO +192 -0
- adaptive_executor-0.1.0/README.md +144 -0
- adaptive_executor-0.1.0/pyproject.toml +104 -0
- adaptive_executor-0.1.0/setup.cfg +4 -0
- adaptive_executor-0.1.0/src/adaptive_executor/__init__.py +29 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/__init__.py +59 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/base.py +111 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/cpu.py +129 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/datetime.py +151 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/memory.py +129 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/multi/__init__.py +18 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/multi/conditional.py +148 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/multi/multi.py +173 -0
- adaptive_executor-0.1.0/src/adaptive_executor/criteria/time.py +149 -0
- adaptive_executor-0.1.0/src/adaptive_executor/executor.py +185 -0
- adaptive_executor-0.1.0/src/adaptive_executor/logger.py +21 -0
- adaptive_executor-0.1.0/src/adaptive_executor/policies.py +82 -0
- adaptive_executor-0.1.0/src/adaptive_executor/utils/__init__.py +9 -0
- adaptive_executor-0.1.0/src/adaptive_executor/utils/logger.py +76 -0
- adaptive_executor-0.1.0/src/adaptive_executor.egg-info/PKG-INFO +192 -0
- adaptive_executor-0.1.0/src/adaptive_executor.egg-info/SOURCES.txt +35 -0
- adaptive_executor-0.1.0/src/adaptive_executor.egg-info/dependency_links.txt +1 -0
- adaptive_executor-0.1.0/src/adaptive_executor.egg-info/requires.txt +27 -0
- adaptive_executor-0.1.0/src/adaptive_executor.egg-info/top_level.txt +1 -0
- adaptive_executor-0.1.0/tests/test_examples_config_management.py +68 -0
- adaptive_executor-0.1.0/tests/test_examples_cpu.py +54 -0
- adaptive_executor-0.1.0/tests/test_examples_datetime.py +39 -0
- adaptive_executor-0.1.0/tests/test_examples_memory.py +56 -0
- adaptive_executor-0.1.0/tests/test_examples_mixed.py +76 -0
- adaptive_executor-0.1.0/tests/test_examples_time.py +68 -0
- adaptive_executor-0.1.0/tests/test_integration_parallelism.py +77 -0
- adaptive_executor-0.1.0/tests/test_unit_criteria.py +723 -0
- adaptive_executor-0.1.0/tests/test_unit_criteria_multi.py +172 -0
- adaptive_executor-0.1.0/tests/test_unit_executor.py +215 -0
- adaptive_executor-0.1.0/tests/test_unit_policies.py +79 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Teut2711
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: adaptive-executor
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Adaptive thread pool executor with dynamic scaling policies
|
|
5
|
+
Author-email: Teut2711 <manglavishesh64@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Teut2711/adaptive-executor
|
|
8
|
+
Project-URL: Documentation, https://Teut2711.github.io/adaptive-executor
|
|
9
|
+
Project-URL: Repository, https://github.com/Teut2711/adaptive-executor
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/Teut2711/adaptive-executor/issues
|
|
11
|
+
Keywords: threading,concurrency,executor,adaptive,scaling,thread,pool
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Provides-Extra: time
|
|
26
|
+
Requires-Dist: pytz>=2023.3; extra == "time"
|
|
27
|
+
Provides-Extra: cpu
|
|
28
|
+
Requires-Dist: psutil>=5.9.0; extra == "cpu"
|
|
29
|
+
Provides-Extra: standard
|
|
30
|
+
Requires-Dist: pytz>=2023.3; extra == "standard"
|
|
31
|
+
Requires-Dist: psutil>=5.9.0; extra == "standard"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
37
|
+
Requires-Dist: freezegun>=1.5.0; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
39
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytz>=2023.3; extra == "dev"
|
|
42
|
+
Requires-Dist: psutil>=5.9.0; extra == "dev"
|
|
43
|
+
Provides-Extra: docs
|
|
44
|
+
Requires-Dist: sphinx>=6.0.0; extra == "docs"
|
|
45
|
+
Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "docs"
|
|
46
|
+
Requires-Dist: myst-parser>=1.0.0; extra == "docs"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Adaptive Executor
|
|
51
|
+
|
|
52
|
+
Adaptive thread pool executor that scales concurrency based on policies.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
- Time-based scaling
|
|
56
|
+
- CPU & memory criteria
|
|
57
|
+
- Multi-criterion aggregation
|
|
58
|
+
- Graceful shutdown
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
**Minimal Installation (Core Only)**
|
|
63
|
+
```bash
|
|
64
|
+
pip install adaptive-executor
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Feature-Specific Installations**
|
|
68
|
+
```bash
|
|
69
|
+
# Time-based scaling only
|
|
70
|
+
pip install adaptive-executor[time]
|
|
71
|
+
|
|
72
|
+
# CPU/Memory monitoring only
|
|
73
|
+
pip install adaptive-executor[cpu]
|
|
74
|
+
|
|
75
|
+
# Complete feature set (recommended)
|
|
76
|
+
pip install adaptive-executor[standard]
|
|
77
|
+
|
|
78
|
+
# Development with all tools
|
|
79
|
+
pip install adaptive-executor[dev]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from adaptive_executor import AdaptiveExecutor, MultiCriterionPolicy
|
|
86
|
+
from adaptive_executor.criteria import TimeCriterion, CpuCriterion
|
|
87
|
+
from datetime import time
|
|
88
|
+
|
|
89
|
+
# Create scaling criteria
|
|
90
|
+
time_policy = TimeCriterion(
|
|
91
|
+
worker_count=8,
|
|
92
|
+
active_start=time(22, 0),
|
|
93
|
+
active_end=time(6, 0),
|
|
94
|
+
timezone="UTC",
|
|
95
|
+
)
|
|
96
|
+
cpu_policy = CpuCriterion(threshold=75, workers=4)
|
|
97
|
+
|
|
98
|
+
# Combine criteria
|
|
99
|
+
policy = MultiCriterionPolicy([time_policy, cpu_policy], hard_cap=10)
|
|
100
|
+
|
|
101
|
+
# Create and use executor
|
|
102
|
+
executor = AdaptiveExecutor(max_workers=15, policy=policy)
|
|
103
|
+
|
|
104
|
+
# Submit tasks
|
|
105
|
+
def my_task(task_id):
|
|
106
|
+
print(f"Processing task {task_id}")
|
|
107
|
+
|
|
108
|
+
for i in range(5):
|
|
109
|
+
executor.submit(my_task, i)
|
|
110
|
+
|
|
111
|
+
executor.join()
|
|
112
|
+
executor.shutdown()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Development Setup
|
|
116
|
+
|
|
117
|
+
This project uses `setuptools` with a `src/` package layout (`src/adaptive_executor`).
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git clone https://github.com/Teut2711/adaptive-executor.git
|
|
121
|
+
cd adaptive-executor
|
|
122
|
+
uv venv
|
|
123
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
124
|
+
pip install -e ".[dev]"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Logging Configuration
|
|
128
|
+
|
|
129
|
+
The adaptive-executor provides comprehensive logging that can be configured to suit your needs. By default, it logs to the console with INFO level.
|
|
130
|
+
|
|
131
|
+
### Basic Logging Control
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from adaptive_executor import setup_logger
|
|
135
|
+
|
|
136
|
+
# Disable all logging
|
|
137
|
+
setup_logger(level=logging.CRITICAL)
|
|
138
|
+
|
|
139
|
+
# Or set a specific log level
|
|
140
|
+
import logging
|
|
141
|
+
setup_logger(level=logging.WARNING) # Only show warnings and above
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Advanced Logging Configuration
|
|
145
|
+
|
|
146
|
+
You can also configure file logging with rotation:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
# Configure file logging with rotation (10MB per file, keep 5 backups)
|
|
150
|
+
setup_logger(
|
|
151
|
+
level=logging.DEBUG,
|
|
152
|
+
log_file="/var/log/adaptive_executor.log",
|
|
153
|
+
max_bytes=10*1024*1024, # 10MB
|
|
154
|
+
backup_count=5
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Environment Variables
|
|
159
|
+
|
|
160
|
+
You can control logging via environment variables:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
164
|
+
ADAPTIVE_EXECUTOR_LOG_LEVEL=WARNING
|
|
165
|
+
|
|
166
|
+
# Enable file logging
|
|
167
|
+
ADAPTIVE_EXECUTOR_LOG_FILE=/path/to/logfile.log
|
|
168
|
+
|
|
169
|
+
# Set max log file size (in bytes)
|
|
170
|
+
ADAPTIVE_EXECUTOR_LOG_MAX_BYTES=10485760 # 10MB
|
|
171
|
+
|
|
172
|
+
# Set number of backup files to keep
|
|
173
|
+
ADAPTIVE_EXECUTOR_LOG_BACKUP_COUNT=5
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Disabling Logging Completely
|
|
177
|
+
|
|
178
|
+
To completely disable all logging:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
import logging
|
|
182
|
+
logging.getLogger('adaptive_executor').addHandler(logging.NullHandler())
|
|
183
|
+
logging.getLogger('adaptive_executor').propagate = False
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Documentation
|
|
187
|
+
|
|
188
|
+
Full documentation is available at [https://Teut2711.github.io/adaptive-executor](https://Teut2711.github.io/adaptive-executor)
|
|
189
|
+
|
|
190
|
+
## License
|
|
191
|
+
|
|
192
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
|
|
2
|
+
# Adaptive Executor
|
|
3
|
+
|
|
4
|
+
Adaptive thread pool executor that scales concurrency based on policies.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
- Time-based scaling
|
|
8
|
+
- CPU & memory criteria
|
|
9
|
+
- Multi-criterion aggregation
|
|
10
|
+
- Graceful shutdown
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
**Minimal Installation (Core Only)**
|
|
15
|
+
```bash
|
|
16
|
+
pip install adaptive-executor
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Feature-Specific Installations**
|
|
20
|
+
```bash
|
|
21
|
+
# Time-based scaling only
|
|
22
|
+
pip install adaptive-executor[time]
|
|
23
|
+
|
|
24
|
+
# CPU/Memory monitoring only
|
|
25
|
+
pip install adaptive-executor[cpu]
|
|
26
|
+
|
|
27
|
+
# Complete feature set (recommended)
|
|
28
|
+
pip install adaptive-executor[standard]
|
|
29
|
+
|
|
30
|
+
# Development with all tools
|
|
31
|
+
pip install adaptive-executor[dev]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from adaptive_executor import AdaptiveExecutor, MultiCriterionPolicy
|
|
38
|
+
from adaptive_executor.criteria import TimeCriterion, CpuCriterion
|
|
39
|
+
from datetime import time
|
|
40
|
+
|
|
41
|
+
# Create scaling criteria
|
|
42
|
+
time_policy = TimeCriterion(
|
|
43
|
+
worker_count=8,
|
|
44
|
+
active_start=time(22, 0),
|
|
45
|
+
active_end=time(6, 0),
|
|
46
|
+
timezone="UTC",
|
|
47
|
+
)
|
|
48
|
+
cpu_policy = CpuCriterion(threshold=75, workers=4)
|
|
49
|
+
|
|
50
|
+
# Combine criteria
|
|
51
|
+
policy = MultiCriterionPolicy([time_policy, cpu_policy], hard_cap=10)
|
|
52
|
+
|
|
53
|
+
# Create and use executor
|
|
54
|
+
executor = AdaptiveExecutor(max_workers=15, policy=policy)
|
|
55
|
+
|
|
56
|
+
# Submit tasks
|
|
57
|
+
def my_task(task_id):
|
|
58
|
+
print(f"Processing task {task_id}")
|
|
59
|
+
|
|
60
|
+
for i in range(5):
|
|
61
|
+
executor.submit(my_task, i)
|
|
62
|
+
|
|
63
|
+
executor.join()
|
|
64
|
+
executor.shutdown()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Development Setup
|
|
68
|
+
|
|
69
|
+
This project uses `setuptools` with a `src/` package layout (`src/adaptive_executor`).
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/Teut2711/adaptive-executor.git
|
|
73
|
+
cd adaptive-executor
|
|
74
|
+
uv venv
|
|
75
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
76
|
+
pip install -e ".[dev]"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Logging Configuration
|
|
80
|
+
|
|
81
|
+
The adaptive-executor provides comprehensive logging that can be configured to suit your needs. By default, it logs to the console with INFO level.
|
|
82
|
+
|
|
83
|
+
### Basic Logging Control
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from adaptive_executor import setup_logger
|
|
87
|
+
|
|
88
|
+
# Disable all logging
|
|
89
|
+
setup_logger(level=logging.CRITICAL)
|
|
90
|
+
|
|
91
|
+
# Or set a specific log level
|
|
92
|
+
import logging
|
|
93
|
+
setup_logger(level=logging.WARNING) # Only show warnings and above
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Advanced Logging Configuration
|
|
97
|
+
|
|
98
|
+
You can also configure file logging with rotation:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
# Configure file logging with rotation (10MB per file, keep 5 backups)
|
|
102
|
+
setup_logger(
|
|
103
|
+
level=logging.DEBUG,
|
|
104
|
+
log_file="/var/log/adaptive_executor.log",
|
|
105
|
+
max_bytes=10*1024*1024, # 10MB
|
|
106
|
+
backup_count=5
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Environment Variables
|
|
111
|
+
|
|
112
|
+
You can control logging via environment variables:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
116
|
+
ADAPTIVE_EXECUTOR_LOG_LEVEL=WARNING
|
|
117
|
+
|
|
118
|
+
# Enable file logging
|
|
119
|
+
ADAPTIVE_EXECUTOR_LOG_FILE=/path/to/logfile.log
|
|
120
|
+
|
|
121
|
+
# Set max log file size (in bytes)
|
|
122
|
+
ADAPTIVE_EXECUTOR_LOG_MAX_BYTES=10485760 # 10MB
|
|
123
|
+
|
|
124
|
+
# Set number of backup files to keep
|
|
125
|
+
ADAPTIVE_EXECUTOR_LOG_BACKUP_COUNT=5
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Disabling Logging Completely
|
|
129
|
+
|
|
130
|
+
To completely disable all logging:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import logging
|
|
134
|
+
logging.getLogger('adaptive_executor').addHandler(logging.NullHandler())
|
|
135
|
+
logging.getLogger('adaptive_executor').propagate = False
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Documentation
|
|
139
|
+
|
|
140
|
+
Full documentation is available at [https://Teut2711.github.io/adaptive-executor](https://Teut2711.github.io/adaptive-executor)
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "adaptive-executor"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Adaptive thread pool executor with dynamic scaling policies"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Teut2711", email = "manglavishesh64@gmail.com"}
|
|
13
|
+
]
|
|
14
|
+
keywords = ["threading", "concurrency", "executor", "adaptive", "scaling", "thread", "pool"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
"Topic :: System :: Distributed Computing",
|
|
26
|
+
]
|
|
27
|
+
requires-python = ">=3.9"
|
|
28
|
+
dependencies = []
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
time = [
|
|
32
|
+
"pytz>=2023.3",
|
|
33
|
+
]
|
|
34
|
+
cpu = [
|
|
35
|
+
"psutil>=5.9.0",
|
|
36
|
+
]
|
|
37
|
+
standard = [
|
|
38
|
+
"pytz>=2023.3",
|
|
39
|
+
"psutil>=5.9.0",
|
|
40
|
+
]
|
|
41
|
+
dev = [
|
|
42
|
+
"pytest>=7.0.0",
|
|
43
|
+
"pytest-cov>=4.0.0",
|
|
44
|
+
"pytest-asyncio>=0.21.0",
|
|
45
|
+
"pytest-mock>=3.10.0",
|
|
46
|
+
"freezegun>=1.5.0",
|
|
47
|
+
"ruff>=0.4.0", # replaces black + isort + flake8
|
|
48
|
+
"mypy>=1.0.0",
|
|
49
|
+
"pre-commit>=3.0.0",
|
|
50
|
+
"pytz>=2023.3",
|
|
51
|
+
"psutil>=5.9.0",
|
|
52
|
+
]
|
|
53
|
+
docs = [
|
|
54
|
+
"sphinx>=6.0.0",
|
|
55
|
+
"sphinx-rtd-theme>=1.2.0",
|
|
56
|
+
"myst-parser>=1.0.0",
|
|
57
|
+
]
|
|
58
|
+
[project.urls]
|
|
59
|
+
Homepage = "https://github.com/Teut2711/adaptive-executor"
|
|
60
|
+
Documentation = "https://Teut2711.github.io/adaptive-executor"
|
|
61
|
+
Repository = "https://github.com/Teut2711/adaptive-executor"
|
|
62
|
+
"Bug Tracker" = "https://github.com/Teut2711/adaptive-executor/issues"
|
|
63
|
+
|
|
64
|
+
[tool.setuptools]
|
|
65
|
+
package-dir = {"" = "src"}
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.packages.find]
|
|
68
|
+
where = ["src"]
|
|
69
|
+
include = ["adaptive_executor*"]
|
|
70
|
+
|
|
71
|
+
[tool.ruff]
|
|
72
|
+
line-length = 88
|
|
73
|
+
target-version = "py39"
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint]
|
|
76
|
+
select = ["E", "F", "I"] # pycodestyle + pyflakes + isort
|
|
77
|
+
|
|
78
|
+
[tool.mypy]
|
|
79
|
+
python_version = "3.9"
|
|
80
|
+
warn_return_any = true
|
|
81
|
+
warn_unused_configs = true
|
|
82
|
+
disallow_untyped_defs = true
|
|
83
|
+
|
|
84
|
+
[tool.pytest.ini_options]
|
|
85
|
+
testpaths = ["tests"]
|
|
86
|
+
python_files = ["test_*.py"]
|
|
87
|
+
python_classes = ["Test*"]
|
|
88
|
+
python_functions = ["test_*"]
|
|
89
|
+
addopts = "--cov=adaptive_executor --cov-report=term-missing"
|
|
90
|
+
markers = [
|
|
91
|
+
"integration: slow integration tests",
|
|
92
|
+
"unit: fast isolated tests",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[tool.coverage.run]
|
|
96
|
+
source = ["adaptive_executor"]
|
|
97
|
+
|
|
98
|
+
[tool.coverage.report]
|
|
99
|
+
exclude_lines = [
|
|
100
|
+
"pragma: no cover",
|
|
101
|
+
"def __repr__",
|
|
102
|
+
"raise AssertionError",
|
|
103
|
+
"raise NotImplementedError",
|
|
104
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .criteria import (
|
|
2
|
+
ConditionalCriterion,
|
|
3
|
+
CpuCriterion,
|
|
4
|
+
DateTimeCriterion,
|
|
5
|
+
MemoryCriterion,
|
|
6
|
+
MultiCriterion,
|
|
7
|
+
ScalingCriterion,
|
|
8
|
+
TimeCriterion,
|
|
9
|
+
from_dict,
|
|
10
|
+
)
|
|
11
|
+
from .executor import AdaptiveExecutor
|
|
12
|
+
from .policies import MultiCriterionPolicy
|
|
13
|
+
from .utils import get_logger, logger, setup_logger
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"AdaptiveExecutor",
|
|
17
|
+
"MultiCriterionPolicy",
|
|
18
|
+
"ScalingCriterion",
|
|
19
|
+
"TimeCriterion",
|
|
20
|
+
"DateTimeCriterion",
|
|
21
|
+
"CpuCriterion",
|
|
22
|
+
"MemoryCriterion",
|
|
23
|
+
"MultiCriterion",
|
|
24
|
+
"ConditionalCriterion",
|
|
25
|
+
"from_dict",
|
|
26
|
+
"get_logger",
|
|
27
|
+
"setup_logger",
|
|
28
|
+
"logger",
|
|
29
|
+
]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Scaling criteria for adaptive executor."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
# Also expose the modules for patching
|
|
6
|
+
from . import cpu, datetime, memory, multi, time
|
|
7
|
+
from .base import ScalingCriterion
|
|
8
|
+
from .cpu import CpuCriterion
|
|
9
|
+
from .datetime import DateTimeCriterion
|
|
10
|
+
from .memory import MemoryCriterion
|
|
11
|
+
from .multi import ConditionalCriterion, MultiCriterion
|
|
12
|
+
from .time import TimeCriterion
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def from_dict(data: Dict[str, Any]) -> ScalingCriterion:
|
|
16
|
+
"""Create a criterion from a dictionary.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
data: Dictionary containing serialized criterion data
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
ScalingCriterion: An instance of the appropriate criterion class
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
ValueError: If the criterion type is unknown
|
|
26
|
+
"""
|
|
27
|
+
criterion_type = data.get("type")
|
|
28
|
+
|
|
29
|
+
if criterion_type == "TimeCriterion":
|
|
30
|
+
return TimeCriterion.from_dict(data)
|
|
31
|
+
elif criterion_type == "DateTimeCriterion":
|
|
32
|
+
return DateTimeCriterion.from_dict(data)
|
|
33
|
+
elif criterion_type == "CpuCriterion":
|
|
34
|
+
return CpuCriterion.from_dict(data)
|
|
35
|
+
elif criterion_type == "MemoryCriterion":
|
|
36
|
+
return MemoryCriterion.from_dict(data)
|
|
37
|
+
elif criterion_type == "MultiCriterion":
|
|
38
|
+
return MultiCriterion.from_dict(data)
|
|
39
|
+
elif criterion_type == "ConditionalCriterion":
|
|
40
|
+
return ConditionalCriterion.from_dict(data)
|
|
41
|
+
else:
|
|
42
|
+
raise ValueError(f"Unknown criterion type: {criterion_type}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"ScalingCriterion",
|
|
47
|
+
"TimeCriterion",
|
|
48
|
+
"DateTimeCriterion",
|
|
49
|
+
"CpuCriterion",
|
|
50
|
+
"MemoryCriterion",
|
|
51
|
+
"MultiCriterion",
|
|
52
|
+
"ConditionalCriterion",
|
|
53
|
+
"from_dict",
|
|
54
|
+
"time",
|
|
55
|
+
"datetime",
|
|
56
|
+
"cpu",
|
|
57
|
+
"memory",
|
|
58
|
+
"multi",
|
|
59
|
+
]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Base class for all scaling criteria."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any, Dict, Type, TypeVar
|
|
6
|
+
|
|
7
|
+
from ..utils import get_logger
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
# Type variable for criterion classes
|
|
12
|
+
C = TypeVar("C", bound="ScalingCriterion")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ScalingCriterion:
|
|
16
|
+
"""Base class for all scaling criteria.
|
|
17
|
+
|
|
18
|
+
Subclasses must implement the max_workers() method to define their scaling logic.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def max_workers(self) -> int:
|
|
22
|
+
"""Calculate the maximum number of workers based on this criterion.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
int: The maximum number of workers (must be at least 1)
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
NotImplementedError: If the subclass doesn't implement this method
|
|
29
|
+
"""
|
|
30
|
+
raise NotImplementedError("Subclasses must implement max_workers()")
|
|
31
|
+
|
|
32
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
33
|
+
"""Serialize criterion to a dictionary.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Dict[str, Any]: A dictionary representation of the criterion
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
NotImplementedError: If the subclass doesn't implement this method
|
|
40
|
+
"""
|
|
41
|
+
raise NotImplementedError("Subclasses must implement to_dict()")
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def from_dict(cls: Type[C], data: Dict[str, Any]) -> C:
|
|
45
|
+
"""Deserialize criterion from a dictionary.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
data: Dictionary containing serialized criterion data
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
An instance of the criterion
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
NotImplementedError: If the subclass doesn't implement this method
|
|
55
|
+
ValueError: If the data is invalid
|
|
56
|
+
"""
|
|
57
|
+
raise NotImplementedError("Subclasses must implement from_dict()")
|
|
58
|
+
|
|
59
|
+
def to_json(self) -> str:
|
|
60
|
+
"""Serialize criterion to a JSON string.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
str: JSON string representation of the criterion
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
json.JSONEncodeError: If the criterion cannot be serialized to JSON
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
return json.dumps(self.to_dict())
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.error(
|
|
72
|
+
"Failed to serialize criterion to JSON: %s", str(e), exc_info=True
|
|
73
|
+
)
|
|
74
|
+
raise
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def from_json(cls: Type[C], json_str: str) -> C:
|
|
78
|
+
"""Deserialize criterion from a JSON string.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
json_str: JSON string containing serialized criterion data
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
An instance of the criterion
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
json.JSONDecodeError: If the JSON string is invalid
|
|
88
|
+
ValueError: If the data is invalid
|
|
89
|
+
NotImplementedError: If the criterion type is not supported
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
data = json.loads(json_str)
|
|
93
|
+
return cls.from_dict(data)
|
|
94
|
+
except json.JSONDecodeError as e:
|
|
95
|
+
logger.error("Invalid JSON string: %s", str(e))
|
|
96
|
+
raise
|
|
97
|
+
except Exception as e:
|
|
98
|
+
logger.error(
|
|
99
|
+
"Failed to deserialize criterion from JSON: %s", str(e), exc_info=True
|
|
100
|
+
)
|
|
101
|
+
raise
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def _parse_datetime(dt_val: str) -> datetime.datetime:
|
|
105
|
+
"""Parse a datetime from string or timestamp."""
|
|
106
|
+
return datetime.datetime.fromisoformat(dt_val)
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def _format_with_tz(dt: datetime.datetime) -> str:
|
|
110
|
+
"""Format a datetime with timezone if available."""
|
|
111
|
+
return dt.isoformat()
|