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.
Files changed (37) hide show
  1. adaptive_executor-0.1.0/LICENSE +21 -0
  2. adaptive_executor-0.1.0/MANIFEST.in +6 -0
  3. adaptive_executor-0.1.0/PKG-INFO +192 -0
  4. adaptive_executor-0.1.0/README.md +144 -0
  5. adaptive_executor-0.1.0/pyproject.toml +104 -0
  6. adaptive_executor-0.1.0/setup.cfg +4 -0
  7. adaptive_executor-0.1.0/src/adaptive_executor/__init__.py +29 -0
  8. adaptive_executor-0.1.0/src/adaptive_executor/criteria/__init__.py +59 -0
  9. adaptive_executor-0.1.0/src/adaptive_executor/criteria/base.py +111 -0
  10. adaptive_executor-0.1.0/src/adaptive_executor/criteria/cpu.py +129 -0
  11. adaptive_executor-0.1.0/src/adaptive_executor/criteria/datetime.py +151 -0
  12. adaptive_executor-0.1.0/src/adaptive_executor/criteria/memory.py +129 -0
  13. adaptive_executor-0.1.0/src/adaptive_executor/criteria/multi/__init__.py +18 -0
  14. adaptive_executor-0.1.0/src/adaptive_executor/criteria/multi/conditional.py +148 -0
  15. adaptive_executor-0.1.0/src/adaptive_executor/criteria/multi/multi.py +173 -0
  16. adaptive_executor-0.1.0/src/adaptive_executor/criteria/time.py +149 -0
  17. adaptive_executor-0.1.0/src/adaptive_executor/executor.py +185 -0
  18. adaptive_executor-0.1.0/src/adaptive_executor/logger.py +21 -0
  19. adaptive_executor-0.1.0/src/adaptive_executor/policies.py +82 -0
  20. adaptive_executor-0.1.0/src/adaptive_executor/utils/__init__.py +9 -0
  21. adaptive_executor-0.1.0/src/adaptive_executor/utils/logger.py +76 -0
  22. adaptive_executor-0.1.0/src/adaptive_executor.egg-info/PKG-INFO +192 -0
  23. adaptive_executor-0.1.0/src/adaptive_executor.egg-info/SOURCES.txt +35 -0
  24. adaptive_executor-0.1.0/src/adaptive_executor.egg-info/dependency_links.txt +1 -0
  25. adaptive_executor-0.1.0/src/adaptive_executor.egg-info/requires.txt +27 -0
  26. adaptive_executor-0.1.0/src/adaptive_executor.egg-info/top_level.txt +1 -0
  27. adaptive_executor-0.1.0/tests/test_examples_config_management.py +68 -0
  28. adaptive_executor-0.1.0/tests/test_examples_cpu.py +54 -0
  29. adaptive_executor-0.1.0/tests/test_examples_datetime.py +39 -0
  30. adaptive_executor-0.1.0/tests/test_examples_memory.py +56 -0
  31. adaptive_executor-0.1.0/tests/test_examples_mixed.py +76 -0
  32. adaptive_executor-0.1.0/tests/test_examples_time.py +68 -0
  33. adaptive_executor-0.1.0/tests/test_integration_parallelism.py +77 -0
  34. adaptive_executor-0.1.0/tests/test_unit_criteria.py +723 -0
  35. adaptive_executor-0.1.0/tests/test_unit_criteria_multi.py +172 -0
  36. adaptive_executor-0.1.0/tests/test_unit_executor.py +215 -0
  37. 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,6 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include src/adaptive_executor *.py
5
+ recursive-exclude * __pycache__
6
+ recursive-exclude * .pyc
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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()