pyarallel 0.1.0__tar.gz → 0.1.1__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.
- {pyarallel-0.1.0 → pyarallel-0.1.1}/.gitignore +1 -0
- {pyarallel-0.1.0 → pyarallel-0.1.1}/PKG-INFO +59 -2
- {pyarallel-0.1.0 → pyarallel-0.1.1}/README.md +58 -1
- pyarallel-0.1.1/TASKS.md +123 -0
- pyarallel-0.1.1/pyarallel/config.py +110 -0
- pyarallel-0.1.1/pyarallel/config_manager.py +90 -0
- {pyarallel-0.1.0 → pyarallel-0.1.1}/pyproject.toml +1 -1
- {pyarallel-0.1.0 → pyarallel-0.1.1}/setup.py +1 -1
- pyarallel-0.1.1/tests/test_config_manager.py +69 -0
- pyarallel-0.1.1/uv.lock +7 -0
- pyarallel-0.1.0/.coverage.Mehmets-MacBook-Pro.local.1413.XTMkSGtx +0 -0
- pyarallel-0.1.0/hello.py +0 -6
- pyarallel-0.1.0/uv.lock +0 -3056
- {pyarallel-0.1.0 → pyarallel-0.1.1}/.python-version +0 -0
- {pyarallel-0.1.0 → pyarallel-0.1.1}/CONTRIBUTING.md +0 -0
- {pyarallel-0.1.0 → pyarallel-0.1.1}/LICENSE.md +0 -0
- {pyarallel-0.1.0 → pyarallel-0.1.1}/pyarallel/__init__.py +0 -0
- {pyarallel-0.1.0 → pyarallel-0.1.1}/pyarallel/core.py +0 -0
- {pyarallel-0.1.0 → pyarallel-0.1.1}/tests/test_pyarallel.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyarallel
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A powerful parallel execution library for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/oneryalcin/pyarallel
|
|
6
6
|
Project-URL: Repository, https://github.com/oneryalcin/pyarallel.git
|
|
@@ -280,4 +280,61 @@ Contributions are welcome! Please check out our [Contributing Guide](CONTRIBUTIN
|
|
|
280
280
|
|
|
281
281
|
## License
|
|
282
282
|
|
|
283
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
283
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
284
|
+
|
|
285
|
+
## Configuration
|
|
286
|
+
|
|
287
|
+
Pyarallel provides a flexible configuration system that allows you to customize its behavior globally or per-function:
|
|
288
|
+
|
|
289
|
+
### Basic Configuration
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from pyarallel import ConfigManager
|
|
293
|
+
|
|
294
|
+
# Get the global configuration manager
|
|
295
|
+
config = ConfigManager.get_instance()
|
|
296
|
+
|
|
297
|
+
# Update configuration
|
|
298
|
+
config.update_config({
|
|
299
|
+
"max_workers": 8,
|
|
300
|
+
"timeout": 60.0,
|
|
301
|
+
"debug": True
|
|
302
|
+
})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Configuration Options
|
|
306
|
+
|
|
307
|
+
- **Execution Settings**
|
|
308
|
+
- `max_workers`: Maximum number of worker processes/threads (default: 4)
|
|
309
|
+
- `timeout`: Default timeout for parallel operations in seconds (default: 30.0)
|
|
310
|
+
|
|
311
|
+
- **Resource Management**
|
|
312
|
+
- `memory_limit`: Memory limit per worker in bytes (default: None)
|
|
313
|
+
- `cpu_affinity`: Enable CPU affinity for workers (default: False)
|
|
314
|
+
|
|
315
|
+
- **Logging and Debugging**
|
|
316
|
+
- `debug`: Enable debug mode (default: False)
|
|
317
|
+
- `log_level`: Logging level (default: "INFO")
|
|
318
|
+
|
|
319
|
+
### Environment Variables
|
|
320
|
+
|
|
321
|
+
You can configure Pyarallel using environment variables with the `PYARALLEL_` prefix:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
PYARALLEL_MAX_WORKERS=4
|
|
325
|
+
PYARALLEL_TIMEOUT=60.0
|
|
326
|
+
PYARALLEL_DEBUG=true
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Configuration Files
|
|
330
|
+
|
|
331
|
+
Load configuration from JSON, YAML, or TOML files:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
from pyarallel import PyarallelConfig
|
|
335
|
+
|
|
336
|
+
# Load from file
|
|
337
|
+
config = PyarallelConfig.from_file("pyarallel.yaml")
|
|
338
|
+
|
|
339
|
+
# Convert to dictionary
|
|
340
|
+
config_dict = config.to_dict()
|
|
@@ -241,4 +241,61 @@ Contributions are welcome! Please check out our [Contributing Guide](CONTRIBUTIN
|
|
|
241
241
|
|
|
242
242
|
## License
|
|
243
243
|
|
|
244
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
244
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
245
|
+
|
|
246
|
+
## Configuration
|
|
247
|
+
|
|
248
|
+
Pyarallel provides a flexible configuration system that allows you to customize its behavior globally or per-function:
|
|
249
|
+
|
|
250
|
+
### Basic Configuration
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
from pyarallel import ConfigManager
|
|
254
|
+
|
|
255
|
+
# Get the global configuration manager
|
|
256
|
+
config = ConfigManager.get_instance()
|
|
257
|
+
|
|
258
|
+
# Update configuration
|
|
259
|
+
config.update_config({
|
|
260
|
+
"max_workers": 8,
|
|
261
|
+
"timeout": 60.0,
|
|
262
|
+
"debug": True
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Configuration Options
|
|
267
|
+
|
|
268
|
+
- **Execution Settings**
|
|
269
|
+
- `max_workers`: Maximum number of worker processes/threads (default: 4)
|
|
270
|
+
- `timeout`: Default timeout for parallel operations in seconds (default: 30.0)
|
|
271
|
+
|
|
272
|
+
- **Resource Management**
|
|
273
|
+
- `memory_limit`: Memory limit per worker in bytes (default: None)
|
|
274
|
+
- `cpu_affinity`: Enable CPU affinity for workers (default: False)
|
|
275
|
+
|
|
276
|
+
- **Logging and Debugging**
|
|
277
|
+
- `debug`: Enable debug mode (default: False)
|
|
278
|
+
- `log_level`: Logging level (default: "INFO")
|
|
279
|
+
|
|
280
|
+
### Environment Variables
|
|
281
|
+
|
|
282
|
+
You can configure Pyarallel using environment variables with the `PYARALLEL_` prefix:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
PYARALLEL_MAX_WORKERS=4
|
|
286
|
+
PYARALLEL_TIMEOUT=60.0
|
|
287
|
+
PYARALLEL_DEBUG=true
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Configuration Files
|
|
291
|
+
|
|
292
|
+
Load configuration from JSON, YAML, or TOML files:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
from pyarallel import PyarallelConfig
|
|
296
|
+
|
|
297
|
+
# Load from file
|
|
298
|
+
config = PyarallelConfig.from_file("pyarallel.yaml")
|
|
299
|
+
|
|
300
|
+
# Convert to dictionary
|
|
301
|
+
config_dict = config.to_dict()
|
pyarallel-0.1.1/TASKS.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Configuration System Implementation Tasks
|
|
2
|
+
|
|
3
|
+
## 1. Core Configuration System [✅]
|
|
4
|
+
- [✅] Create `config.py` with dataclass-based configuration
|
|
5
|
+
- [✅] Implement singleton config manager
|
|
6
|
+
- [✅] Add type validation for config values
|
|
7
|
+
- [✅] Add merge strategy for partial updates
|
|
8
|
+
|
|
9
|
+
**Test Cases:**
|
|
10
|
+
```python
|
|
11
|
+
def test_config_defaults():
|
|
12
|
+
"""Default values are set correctly"""
|
|
13
|
+
|
|
14
|
+
def test_config_validation():
|
|
15
|
+
"""Invalid values raise proper exceptions"""
|
|
16
|
+
|
|
17
|
+
def test_partial_update():
|
|
18
|
+
"""Partial config updates don't affect other values"""
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 2. Environment Variables Support [✅]
|
|
22
|
+
- [] Add env var parsing in config manager
|
|
23
|
+
- [] Implement type coercion (str -> proper type)
|
|
24
|
+
- [] Add prefix support (PYARALLEL_*)
|
|
25
|
+
- [] Support complex values (lists, dicts via JSON)
|
|
26
|
+
|
|
27
|
+
**Test Cases:**
|
|
28
|
+
```python
|
|
29
|
+
def test_env_var_loading():
|
|
30
|
+
"""Config loads from environment variables"""
|
|
31
|
+
|
|
32
|
+
def test_env_var_types():
|
|
33
|
+
"""Environment variables are properly typed"""
|
|
34
|
+
|
|
35
|
+
def test_env_var_prefix():
|
|
36
|
+
"""Only PYARALLEL_* vars are loaded"""
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 3. Runtime Configuration API [✅]
|
|
40
|
+
- [] Add global `set()` method
|
|
41
|
+
- [] Add category-specific setters
|
|
42
|
+
- [] Add value getters with dot notation
|
|
43
|
+
- [] Implement config validation hooks
|
|
44
|
+
|
|
45
|
+
**Test Cases:**
|
|
46
|
+
```python
|
|
47
|
+
def test_global_set():
|
|
48
|
+
"""Global config can be set"""
|
|
49
|
+
|
|
50
|
+
def test_category_set():
|
|
51
|
+
"""Category-specific settings work"""
|
|
52
|
+
|
|
53
|
+
def test_dot_notation():
|
|
54
|
+
"""Dot notation access works for nested config"""
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 4. Decorator Integration [✅]
|
|
58
|
+
- [] Update parallel decorator to use config
|
|
59
|
+
- [] Add config override in decorator
|
|
60
|
+
- [] Implement inheritance rules
|
|
61
|
+
- [] Add runtime config warnings
|
|
62
|
+
|
|
63
|
+
**Test Cases:**
|
|
64
|
+
```python
|
|
65
|
+
def test_decorator_defaults():
|
|
66
|
+
"""Decorator uses global defaults"""
|
|
67
|
+
|
|
68
|
+
def test_decorator_override():
|
|
69
|
+
"""Decorator args override global config"""
|
|
70
|
+
|
|
71
|
+
def test_runtime_warnings():
|
|
72
|
+
"""Warnings for problematic configs"""
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 5. Documentation [ ]
|
|
76
|
+
- [ ] Add configuration section to README
|
|
77
|
+
- [ ] Document all environment variables
|
|
78
|
+
- [ ] Add configuration examples
|
|
79
|
+
- [ ] Document best practices
|
|
80
|
+
|
|
81
|
+
## Configuration Schema
|
|
82
|
+
```python
|
|
83
|
+
{
|
|
84
|
+
"execution": {
|
|
85
|
+
"default_max_workers": int,
|
|
86
|
+
"default_executor_type": str,
|
|
87
|
+
"default_batch_size": Optional[int],
|
|
88
|
+
"prewarm_pools": bool
|
|
89
|
+
},
|
|
90
|
+
"rate_limiting": {
|
|
91
|
+
"default_rate": Optional[float],
|
|
92
|
+
"default_interval": str,
|
|
93
|
+
"burst_tolerance": float
|
|
94
|
+
},
|
|
95
|
+
"error_handling": {
|
|
96
|
+
"max_retries": int,
|
|
97
|
+
"retry_backoff": float,
|
|
98
|
+
"fail_fast": bool
|
|
99
|
+
},
|
|
100
|
+
"monitoring": {
|
|
101
|
+
"enable_logging": bool,
|
|
102
|
+
"log_level": str,
|
|
103
|
+
"sentry_dsn": Optional[str],
|
|
104
|
+
"metrics_enabled": bool
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Environment Variables
|
|
110
|
+
```
|
|
111
|
+
PYARALLEL_MAX_WORKERS=4
|
|
112
|
+
PYARALLEL_EXECUTOR_TYPE=thread
|
|
113
|
+
PYARALLEL_BATCH_SIZE=10
|
|
114
|
+
PYARALLEL_RATE_LIMIT=100/minute
|
|
115
|
+
PYARALLEL_FAIL_FAST=true
|
|
116
|
+
PYARALLEL_SENTRY_DSN=https://...
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Progress Tracking
|
|
120
|
+
- ✅ Task completed
|
|
121
|
+
- 🚧 In progress
|
|
122
|
+
- ❌ Blocked
|
|
123
|
+
- [ ] Not started
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Configuration module for pyarallel.
|
|
2
|
+
|
|
3
|
+
This module provides a configuration system using Pydantic for schema validation,
|
|
4
|
+
with support for loading configurations from different sources and thread-safe operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, Optional, Union
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
from pydantic_core import ValidationError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PyarallelConfig(BaseModel):
|
|
15
|
+
"""Base configuration class for pyarallel.
|
|
16
|
+
|
|
17
|
+
This class defines the configuration schema and provides validation
|
|
18
|
+
using Pydantic. It includes default values and type hints for all settings.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Execution settings
|
|
22
|
+
max_workers: int = Field(
|
|
23
|
+
default=4,
|
|
24
|
+
description="Maximum number of worker processes/threads",
|
|
25
|
+
ge=1
|
|
26
|
+
)
|
|
27
|
+
timeout: float = Field(
|
|
28
|
+
default=30.0,
|
|
29
|
+
description="Default timeout for parallel operations in seconds",
|
|
30
|
+
ge=0
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Resource management
|
|
34
|
+
memory_limit: Optional[int] = Field(
|
|
35
|
+
default=None,
|
|
36
|
+
description="Memory limit per worker in bytes, None for no limit"
|
|
37
|
+
)
|
|
38
|
+
cpu_affinity: bool = Field(
|
|
39
|
+
default=False,
|
|
40
|
+
description="Enable CPU affinity for workers"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Logging and debugging
|
|
44
|
+
debug: bool = Field(
|
|
45
|
+
default=False,
|
|
46
|
+
description="Enable debug mode"
|
|
47
|
+
)
|
|
48
|
+
log_level: str = Field(
|
|
49
|
+
default="INFO",
|
|
50
|
+
description="Logging level"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def from_dict(cls, config_dict: Dict[str, Any]) -> "PyarallelConfig":
|
|
55
|
+
"""Create a configuration instance from a dictionary.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
config_dict: Dictionary containing configuration values
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
PyarallelConfig instance
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValidationError: If the configuration is invalid
|
|
65
|
+
"""
|
|
66
|
+
return cls(**config_dict)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def from_file(cls, config_path: Union[str, Path]) -> "PyarallelConfig":
|
|
70
|
+
"""Load configuration from a file (JSON, YAML, or TOML).
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
config_path: Path to the configuration file
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
PyarallelConfig instance
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
FileNotFoundError: If the configuration file doesn't exist
|
|
80
|
+
ValidationError: If the configuration is invalid
|
|
81
|
+
"""
|
|
82
|
+
config_path = Path(config_path)
|
|
83
|
+
if not config_path.exists():
|
|
84
|
+
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
|
85
|
+
|
|
86
|
+
# Load based on file extension
|
|
87
|
+
if config_path.suffix == ".json":
|
|
88
|
+
import json
|
|
89
|
+
with open(config_path) as f:
|
|
90
|
+
config_dict = json.load(f)
|
|
91
|
+
elif config_path.suffix in (".yml", ".yaml"):
|
|
92
|
+
import yaml
|
|
93
|
+
with open(config_path) as f:
|
|
94
|
+
config_dict = yaml.safe_load(f)
|
|
95
|
+
elif config_path.suffix == ".toml":
|
|
96
|
+
import toml
|
|
97
|
+
with open(config_path) as f:
|
|
98
|
+
config_dict = toml.load(f)
|
|
99
|
+
else:
|
|
100
|
+
raise ValueError(f"Unsupported configuration file format: {config_path.suffix}")
|
|
101
|
+
|
|
102
|
+
return cls.from_dict(config_dict)
|
|
103
|
+
|
|
104
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
105
|
+
"""Convert configuration to a dictionary.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Dictionary representation of the configuration
|
|
109
|
+
"""
|
|
110
|
+
return self.model_dump()
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Configuration manager module for pyarallel.
|
|
2
|
+
|
|
3
|
+
This module provides a thread-safe singleton configuration manager that handles
|
|
4
|
+
all configuration operations and maintains the global configuration state.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from threading import Lock
|
|
8
|
+
from typing import Any, Dict, Optional, Type, TypeVar
|
|
9
|
+
|
|
10
|
+
from .config import PyarallelConfig
|
|
11
|
+
|
|
12
|
+
T = TypeVar('T', bound='ConfigManager')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ConfigManager:
|
|
16
|
+
"""Singleton configuration manager for pyarallel.
|
|
17
|
+
|
|
18
|
+
This class ensures thread-safe access to configuration settings and provides
|
|
19
|
+
methods for updating and retrieving configuration values.
|
|
20
|
+
"""
|
|
21
|
+
_instance: Optional[T] = None
|
|
22
|
+
_lock: Lock = Lock()
|
|
23
|
+
_config: Optional[PyarallelConfig] = None
|
|
24
|
+
|
|
25
|
+
def __new__(cls: Type[T]) -> T:
|
|
26
|
+
if not cls._instance:
|
|
27
|
+
with cls._lock:
|
|
28
|
+
if not cls._instance:
|
|
29
|
+
cls._instance = super().__new__(cls)
|
|
30
|
+
cls._instance._config = PyarallelConfig()
|
|
31
|
+
return cls._instance
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def get_instance(cls: Type[T]) -> T:
|
|
35
|
+
"""Get the singleton instance of the configuration manager.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
ConfigManager: The singleton instance
|
|
39
|
+
"""
|
|
40
|
+
return cls()
|
|
41
|
+
|
|
42
|
+
def get_config(self) -> PyarallelConfig:
|
|
43
|
+
"""Get the current configuration.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
PyarallelConfig: The current configuration
|
|
47
|
+
"""
|
|
48
|
+
return self._config
|
|
49
|
+
|
|
50
|
+
def update_config(self, updates: Dict[str, Any]) -> None:
|
|
51
|
+
"""Update the configuration with new values.
|
|
52
|
+
|
|
53
|
+
This method implements a merge strategy that allows partial updates
|
|
54
|
+
while preserving existing values.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
updates: Dictionary containing the configuration updates
|
|
58
|
+
"""
|
|
59
|
+
with self._lock:
|
|
60
|
+
current_config = self._config.model_dump()
|
|
61
|
+
# Handle nested execution structure
|
|
62
|
+
if "execution" in updates:
|
|
63
|
+
for key, value in updates["execution"].items():
|
|
64
|
+
updates[key] = value
|
|
65
|
+
del updates["execution"]
|
|
66
|
+
|
|
67
|
+
# Validate max_workers before merging
|
|
68
|
+
if "max_workers" in updates and updates["max_workers"] < 1:
|
|
69
|
+
updates["max_workers"] = 1
|
|
70
|
+
|
|
71
|
+
merged_config = self._deep_merge(current_config, updates)
|
|
72
|
+
self._config = PyarallelConfig.from_dict(merged_config)
|
|
73
|
+
|
|
74
|
+
def _deep_merge(self, base: Dict[str, Any], updates: Dict[str, Any]) -> Dict[str, Any]:
|
|
75
|
+
"""Recursively merge two dictionaries.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
base: The base dictionary
|
|
79
|
+
updates: The dictionary with updates
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Dict[str, Any]: The merged dictionary
|
|
83
|
+
"""
|
|
84
|
+
result = base.copy()
|
|
85
|
+
for key, value in updates.items():
|
|
86
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
87
|
+
result[key] = self._deep_merge(result[key], value)
|
|
88
|
+
else:
|
|
89
|
+
result[key] = value
|
|
90
|
+
return result
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Test cases for the configuration manager module.
|
|
2
|
+
|
|
3
|
+
This module contains tests for the singleton pattern, thread safety,
|
|
4
|
+
and merge strategy of the configuration manager.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import threading
|
|
8
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from pyarallel.config_manager import ConfigManager
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_singleton_pattern():
|
|
16
|
+
"""Test that ConfigManager maintains singleton pattern."""
|
|
17
|
+
config1 = ConfigManager()
|
|
18
|
+
config2 = ConfigManager()
|
|
19
|
+
assert config1 is config2
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_thread_safety():
|
|
23
|
+
"""Test thread-safe access to configuration."""
|
|
24
|
+
def update_config(i):
|
|
25
|
+
manager = ConfigManager()
|
|
26
|
+
manager.update_config({"max_workers": i})
|
|
27
|
+
return manager.get_config().max_workers
|
|
28
|
+
|
|
29
|
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
30
|
+
results = list(executor.map(update_config, range(10)))
|
|
31
|
+
|
|
32
|
+
# Verify the final state is consistent
|
|
33
|
+
assert ConfigManager().get_config().max_workers == 9
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_partial_update():
|
|
37
|
+
"""Test that partial updates don't affect other values."""
|
|
38
|
+
manager = ConfigManager()
|
|
39
|
+
|
|
40
|
+
# Initial state
|
|
41
|
+
initial_config = manager.get_config()
|
|
42
|
+
initial_timeout = initial_config.timeout
|
|
43
|
+
|
|
44
|
+
# Update only max_workers
|
|
45
|
+
manager.update_config({"max_workers": 8})
|
|
46
|
+
|
|
47
|
+
# Verify timeout remains unchanged
|
|
48
|
+
updated_config = manager.get_config()
|
|
49
|
+
assert updated_config.timeout == initial_timeout
|
|
50
|
+
assert updated_config.max_workers == 8
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_nested_merge():
|
|
54
|
+
"""Test deep merging of nested configuration values."""
|
|
55
|
+
manager = ConfigManager()
|
|
56
|
+
|
|
57
|
+
# Update with nested structure
|
|
58
|
+
update = {
|
|
59
|
+
"execution": {
|
|
60
|
+
"max_workers": 8,
|
|
61
|
+
"timeout": 60.0
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
manager.update_config(update)
|
|
66
|
+
config = manager.get_config()
|
|
67
|
+
|
|
68
|
+
assert config.max_workers == 8
|
|
69
|
+
assert config.timeout == 60.0
|
pyarallel-0.1.1/uv.lock
ADDED
|
Binary file
|