configsync-lite 0.1.2__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.
- configsync_lite-0.1.2/PKG-INFO +116 -0
- configsync_lite-0.1.2/README.md +95 -0
- configsync_lite-0.1.2/configsync_lite/__init__.py +13 -0
- configsync_lite-0.1.2/configsync_lite/core.py +80 -0
- configsync_lite-0.1.2/configsync_lite/env.py +35 -0
- configsync_lite-0.1.2/configsync_lite/validators.py +35 -0
- configsync_lite-0.1.2/configsync_lite.egg-info/PKG-INFO +116 -0
- configsync_lite-0.1.2/configsync_lite.egg-info/SOURCES.txt +11 -0
- configsync_lite-0.1.2/configsync_lite.egg-info/dependency_links.txt +1 -0
- configsync_lite-0.1.2/configsync_lite.egg-info/top_level.txt +1 -0
- configsync_lite-0.1.2/pyproject.toml +35 -0
- configsync_lite-0.1.2/setup.cfg +4 -0
- configsync_lite-0.1.2/tests/test_core.py +82 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: configsync-lite
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A lightweight configuration sync utility with environment variable interpolation
|
|
5
|
+
Author-email: optimusdemo <akrine2000@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/akrine2000/configsync-lite
|
|
8
|
+
Project-URL: Repository, https://github.com/akrine2000/configsync-lite
|
|
9
|
+
Project-URL: Issues, https://github.com/akrine2000/configsync-lite/issues
|
|
10
|
+
Keywords: config,configuration,sync,environment,yaml,json
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# configsync-lite
|
|
23
|
+
|
|
24
|
+
A lightweight Python library for managing configuration files with environment variable interpolation and schema validation.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install configsync-lite
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from configsync_lite import ConfigSync
|
|
36
|
+
|
|
37
|
+
cfg = ConfigSync("config.json")
|
|
38
|
+
db_host = cfg.get("database.host")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- JSON configuration file support
|
|
44
|
+
- Dot notation access (`cfg.get("database.host")`)
|
|
45
|
+
- Environment variable interpolation (`${DB_HOST}`)
|
|
46
|
+
- Schema validation
|
|
47
|
+
- Deep merge of multiple configs
|
|
48
|
+
- Zero dependencies
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
### Basic Configuration
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from configsync_lite import ConfigSync, load_config
|
|
56
|
+
|
|
57
|
+
# Load from file
|
|
58
|
+
cfg = ConfigSync("myapp.json")
|
|
59
|
+
|
|
60
|
+
# Get values with dot notation
|
|
61
|
+
host = cfg.get("server.host", default="localhost")
|
|
62
|
+
port = cfg.get("server.port", default=8080)
|
|
63
|
+
|
|
64
|
+
# Set and save
|
|
65
|
+
cfg.set("server.port", 9090)
|
|
66
|
+
cfg.save()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Environment Variable Interpolation
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from configsync_lite import load_config, interpolate_env
|
|
73
|
+
|
|
74
|
+
raw = load_config("config.json")
|
|
75
|
+
# config.json contains: {"db": {"password": "${DB_PASSWORD}"}}
|
|
76
|
+
|
|
77
|
+
resolved = interpolate_env(raw)
|
|
78
|
+
# Replaces ${DB_PASSWORD} with actual env var value
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Merging Configs
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from configsync_lite import merge_configs
|
|
85
|
+
|
|
86
|
+
base = {"server": {"host": "localhost", "port": 8080}}
|
|
87
|
+
override = {"server": {"port": 9090}, "debug": True}
|
|
88
|
+
|
|
89
|
+
merged = merge_configs(base, override)
|
|
90
|
+
# {"server": {"host": "localhost", "port": 9090}, "debug": True}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Schema Validation
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from configsync_lite import load_config, validate_schema
|
|
97
|
+
|
|
98
|
+
config = load_config("config.json")
|
|
99
|
+
schema = {
|
|
100
|
+
"host": {"type": "str", "required": True},
|
|
101
|
+
"port": {"type": "int", "required": True},
|
|
102
|
+
"debug": {"type": "bool", "required": False}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
errors = validate_schema(config, schema)
|
|
106
|
+
if errors:
|
|
107
|
+
print("Config errors:", errors)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Contributing
|
|
111
|
+
|
|
112
|
+
See [CONTRIBUTING.md](docs/CONTRIBUTING.md) for development setup and guidelines.
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# configsync-lite
|
|
2
|
+
|
|
3
|
+
A lightweight Python library for managing configuration files with environment variable interpolation and schema validation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install configsync-lite
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from configsync_lite import ConfigSync
|
|
15
|
+
|
|
16
|
+
cfg = ConfigSync("config.json")
|
|
17
|
+
db_host = cfg.get("database.host")
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- JSON configuration file support
|
|
23
|
+
- Dot notation access (`cfg.get("database.host")`)
|
|
24
|
+
- Environment variable interpolation (`${DB_HOST}`)
|
|
25
|
+
- Schema validation
|
|
26
|
+
- Deep merge of multiple configs
|
|
27
|
+
- Zero dependencies
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Basic Configuration
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from configsync_lite import ConfigSync, load_config
|
|
35
|
+
|
|
36
|
+
# Load from file
|
|
37
|
+
cfg = ConfigSync("myapp.json")
|
|
38
|
+
|
|
39
|
+
# Get values with dot notation
|
|
40
|
+
host = cfg.get("server.host", default="localhost")
|
|
41
|
+
port = cfg.get("server.port", default=8080)
|
|
42
|
+
|
|
43
|
+
# Set and save
|
|
44
|
+
cfg.set("server.port", 9090)
|
|
45
|
+
cfg.save()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Environment Variable Interpolation
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from configsync_lite import load_config, interpolate_env
|
|
52
|
+
|
|
53
|
+
raw = load_config("config.json")
|
|
54
|
+
# config.json contains: {"db": {"password": "${DB_PASSWORD}"}}
|
|
55
|
+
|
|
56
|
+
resolved = interpolate_env(raw)
|
|
57
|
+
# Replaces ${DB_PASSWORD} with actual env var value
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Merging Configs
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from configsync_lite import merge_configs
|
|
64
|
+
|
|
65
|
+
base = {"server": {"host": "localhost", "port": 8080}}
|
|
66
|
+
override = {"server": {"port": 9090}, "debug": True}
|
|
67
|
+
|
|
68
|
+
merged = merge_configs(base, override)
|
|
69
|
+
# {"server": {"host": "localhost", "port": 9090}, "debug": True}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Schema Validation
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from configsync_lite import load_config, validate_schema
|
|
76
|
+
|
|
77
|
+
config = load_config("config.json")
|
|
78
|
+
schema = {
|
|
79
|
+
"host": {"type": "str", "required": True},
|
|
80
|
+
"port": {"type": "int", "required": True},
|
|
81
|
+
"debug": {"type": "bool", "required": False}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
errors = validate_schema(config, schema)
|
|
85
|
+
if errors:
|
|
86
|
+
print("Config errors:", errors)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
90
|
+
|
|
91
|
+
See [CONTRIBUTING.md](docs/CONTRIBUTING.md) for development setup and guidelines.
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
configsync-lite: A lightweight configuration sync utility for Python projects.
|
|
3
|
+
Supports JSON, YAML, and TOML configuration files with environment variable interpolation.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__version__ = "0.1.2"
|
|
7
|
+
__author__ = "optimusdemo"
|
|
8
|
+
|
|
9
|
+
from .core import ConfigSync, load_config, merge_configs
|
|
10
|
+
from .validators import validate_schema
|
|
11
|
+
from .env import interpolate_env
|
|
12
|
+
|
|
13
|
+
__all__ = ["ConfigSync", "load_config", "merge_configs", "validate_schema", "interpolate_env"]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core configuration sync functionality.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from typing import Any, Dict, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConfigSync:
|
|
11
|
+
"""
|
|
12
|
+
Lightweight configuration manager with environment variable support.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
>>> cfg = ConfigSync("config.json")
|
|
16
|
+
>>> cfg.get("database.host")
|
|
17
|
+
'localhost'
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, config_path: str, env_prefix: str = "APP"):
|
|
21
|
+
self.config_path = config_path
|
|
22
|
+
self.env_prefix = env_prefix
|
|
23
|
+
self._config: Dict[str, Any] = {}
|
|
24
|
+
self._load()
|
|
25
|
+
|
|
26
|
+
def _load(self):
|
|
27
|
+
if not os.path.exists(self.config_path):
|
|
28
|
+
return
|
|
29
|
+
with open(self.config_path, "r") as f:
|
|
30
|
+
self._config = json.load(f)
|
|
31
|
+
|
|
32
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
33
|
+
"""Get a config value using dot notation."""
|
|
34
|
+
keys = key.split(".")
|
|
35
|
+
val = self._config
|
|
36
|
+
for k in keys:
|
|
37
|
+
if isinstance(val, dict):
|
|
38
|
+
val = val.get(k)
|
|
39
|
+
else:
|
|
40
|
+
return default
|
|
41
|
+
return val if val is not None else default
|
|
42
|
+
|
|
43
|
+
def set(self, key: str, value: Any):
|
|
44
|
+
"""Set a config value using dot notation."""
|
|
45
|
+
keys = key.split(".")
|
|
46
|
+
d = self._config
|
|
47
|
+
for k in keys[:-1]:
|
|
48
|
+
d = d.setdefault(k, {})
|
|
49
|
+
d[keys[-1]] = value
|
|
50
|
+
|
|
51
|
+
def save(self):
|
|
52
|
+
"""Persist config to disk."""
|
|
53
|
+
with open(self.config_path, "w") as f:
|
|
54
|
+
json.dump(self._config, f, indent=2)
|
|
55
|
+
|
|
56
|
+
def all(self) -> Dict[str, Any]:
|
|
57
|
+
return self._config
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def load_config(path: str) -> Dict[str, Any]:
|
|
61
|
+
"""Load a JSON config file and return as dict."""
|
|
62
|
+
with open(path, "r") as f:
|
|
63
|
+
return json.load(f)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def merge_configs(*configs: Dict[str, Any]) -> Dict[str, Any]:
|
|
67
|
+
"""Deep merge multiple config dicts. Later values take precedence."""
|
|
68
|
+
result = {}
|
|
69
|
+
for config in configs:
|
|
70
|
+
_deep_merge(result, config)
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _deep_merge(base: Dict, override: Dict) -> Dict:
|
|
75
|
+
for k, v in override.items():
|
|
76
|
+
if k in base and isinstance(base[k], dict) and isinstance(v, dict):
|
|
77
|
+
_deep_merge(base[k], v)
|
|
78
|
+
else:
|
|
79
|
+
base[k] = v
|
|
80
|
+
return base
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Environment variable interpolation for config values.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def interpolate_env(config: Dict[str, Any], prefix: str = "") -> Dict[str, Any]:
|
|
11
|
+
"""
|
|
12
|
+
Replace ${VAR} placeholders in config values with environment variables.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
>>> os.environ["DB_HOST"] = "localhost"
|
|
16
|
+
>>> interpolate_env({"host": "${DB_HOST}"})
|
|
17
|
+
{'host': 'localhost'}
|
|
18
|
+
"""
|
|
19
|
+
result = {}
|
|
20
|
+
for k, v in config.items():
|
|
21
|
+
if isinstance(v, dict):
|
|
22
|
+
result[k] = interpolate_env(v, prefix)
|
|
23
|
+
elif isinstance(v, str):
|
|
24
|
+
result[k] = _replace_vars(v)
|
|
25
|
+
else:
|
|
26
|
+
result[k] = v
|
|
27
|
+
return result
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _replace_vars(value: str) -> str:
|
|
31
|
+
pattern = re.compile(r"\$\{([^}]+)\}")
|
|
32
|
+
def replacer(match):
|
|
33
|
+
var_name = match.group(1)
|
|
34
|
+
return os.environ.get(var_name, match.group(0))
|
|
35
|
+
return pattern.sub(replacer, value)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schema validation for configuration files.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def validate_schema(config: Dict[str, Any], schema: Dict[str, Any]) -> List[str]:
|
|
9
|
+
"""
|
|
10
|
+
Validate a config dict against a simple schema.
|
|
11
|
+
Returns a list of validation errors (empty if valid).
|
|
12
|
+
|
|
13
|
+
Schema format:
|
|
14
|
+
{
|
|
15
|
+
"field_name": {"type": "str", "required": True},
|
|
16
|
+
"port": {"type": "int", "required": False, "default": 8080}
|
|
17
|
+
}
|
|
18
|
+
"""
|
|
19
|
+
errors = []
|
|
20
|
+
for field, rules in schema.items():
|
|
21
|
+
value = config.get(field)
|
|
22
|
+
required = rules.get("required", False)
|
|
23
|
+
expected_type = rules.get("type")
|
|
24
|
+
|
|
25
|
+
if value is None:
|
|
26
|
+
if required:
|
|
27
|
+
errors.append(f"Missing required field: '{field}'")
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
type_map = {"str": str, "int": int, "float": float, "bool": bool, "list": list, "dict": dict}
|
|
31
|
+
if expected_type and expected_type in type_map:
|
|
32
|
+
if not isinstance(value, type_map[expected_type]):
|
|
33
|
+
errors.append(f"Field '{field}' expected {expected_type}, got {type(value).__name__}")
|
|
34
|
+
|
|
35
|
+
return errors
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: configsync-lite
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A lightweight configuration sync utility with environment variable interpolation
|
|
5
|
+
Author-email: optimusdemo <akrine2000@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/akrine2000/configsync-lite
|
|
8
|
+
Project-URL: Repository, https://github.com/akrine2000/configsync-lite
|
|
9
|
+
Project-URL: Issues, https://github.com/akrine2000/configsync-lite/issues
|
|
10
|
+
Keywords: config,configuration,sync,environment,yaml,json
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# configsync-lite
|
|
23
|
+
|
|
24
|
+
A lightweight Python library for managing configuration files with environment variable interpolation and schema validation.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install configsync-lite
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from configsync_lite import ConfigSync
|
|
36
|
+
|
|
37
|
+
cfg = ConfigSync("config.json")
|
|
38
|
+
db_host = cfg.get("database.host")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- JSON configuration file support
|
|
44
|
+
- Dot notation access (`cfg.get("database.host")`)
|
|
45
|
+
- Environment variable interpolation (`${DB_HOST}`)
|
|
46
|
+
- Schema validation
|
|
47
|
+
- Deep merge of multiple configs
|
|
48
|
+
- Zero dependencies
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
### Basic Configuration
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from configsync_lite import ConfigSync, load_config
|
|
56
|
+
|
|
57
|
+
# Load from file
|
|
58
|
+
cfg = ConfigSync("myapp.json")
|
|
59
|
+
|
|
60
|
+
# Get values with dot notation
|
|
61
|
+
host = cfg.get("server.host", default="localhost")
|
|
62
|
+
port = cfg.get("server.port", default=8080)
|
|
63
|
+
|
|
64
|
+
# Set and save
|
|
65
|
+
cfg.set("server.port", 9090)
|
|
66
|
+
cfg.save()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Environment Variable Interpolation
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from configsync_lite import load_config, interpolate_env
|
|
73
|
+
|
|
74
|
+
raw = load_config("config.json")
|
|
75
|
+
# config.json contains: {"db": {"password": "${DB_PASSWORD}"}}
|
|
76
|
+
|
|
77
|
+
resolved = interpolate_env(raw)
|
|
78
|
+
# Replaces ${DB_PASSWORD} with actual env var value
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Merging Configs
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from configsync_lite import merge_configs
|
|
85
|
+
|
|
86
|
+
base = {"server": {"host": "localhost", "port": 8080}}
|
|
87
|
+
override = {"server": {"port": 9090}, "debug": True}
|
|
88
|
+
|
|
89
|
+
merged = merge_configs(base, override)
|
|
90
|
+
# {"server": {"host": "localhost", "port": 9090}, "debug": True}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Schema Validation
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from configsync_lite import load_config, validate_schema
|
|
97
|
+
|
|
98
|
+
config = load_config("config.json")
|
|
99
|
+
schema = {
|
|
100
|
+
"host": {"type": "str", "required": True},
|
|
101
|
+
"port": {"type": "int", "required": True},
|
|
102
|
+
"debug": {"type": "bool", "required": False}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
errors = validate_schema(config, schema)
|
|
106
|
+
if errors:
|
|
107
|
+
print("Config errors:", errors)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Contributing
|
|
111
|
+
|
|
112
|
+
See [CONTRIBUTING.md](docs/CONTRIBUTING.md) for development setup and guidelines.
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
configsync_lite/__init__.py
|
|
4
|
+
configsync_lite/core.py
|
|
5
|
+
configsync_lite/env.py
|
|
6
|
+
configsync_lite/validators.py
|
|
7
|
+
configsync_lite.egg-info/PKG-INFO
|
|
8
|
+
configsync_lite.egg-info/SOURCES.txt
|
|
9
|
+
configsync_lite.egg-info/dependency_links.txt
|
|
10
|
+
configsync_lite.egg-info/top_level.txt
|
|
11
|
+
tests/test_core.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
configsync_lite
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "configsync-lite"
|
|
7
|
+
version = "0.1.2"
|
|
8
|
+
description = "A lightweight configuration sync utility with environment variable interpolation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "optimusdemo", email = "akrine2000@gmail.com"}
|
|
13
|
+
]
|
|
14
|
+
keywords = ["config", "configuration", "sync", "environment", "yaml", "json"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.9"
|
|
26
|
+
dependencies = []
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/akrine2000/configsync-lite"
|
|
30
|
+
Repository = "https://github.com/akrine2000/configsync-lite"
|
|
31
|
+
Issues = "https://github.com/akrine2000/configsync-lite/issues"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["."]
|
|
35
|
+
include = ["configsync_lite*"]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Tests for configsync-lite core functionality."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from configsync_lite import ConfigSync, load_config, merge_configs, validate_schema, interpolate_env
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def tmp_config(tmp_path):
|
|
13
|
+
config = {
|
|
14
|
+
"server": {"host": "localhost", "port": 8080},
|
|
15
|
+
"database": {"url": "${DB_URL}", "pool_size": 5},
|
|
16
|
+
"debug": False
|
|
17
|
+
}
|
|
18
|
+
path = tmp_path / "config.json"
|
|
19
|
+
path.write_text(json.dumps(config))
|
|
20
|
+
return str(path)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_load_and_get(tmp_config):
|
|
24
|
+
cfg = ConfigSync(tmp_config)
|
|
25
|
+
assert cfg.get("server.host") == "localhost"
|
|
26
|
+
assert cfg.get("server.port") == 8080
|
|
27
|
+
assert cfg.get("debug") is False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_get_default(tmp_config):
|
|
31
|
+
cfg = ConfigSync(tmp_config)
|
|
32
|
+
assert cfg.get("nonexistent.key", default="fallback") == "fallback"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_set_and_save(tmp_config):
|
|
36
|
+
cfg = ConfigSync(tmp_config)
|
|
37
|
+
cfg.set("server.port", 9090)
|
|
38
|
+
cfg.save()
|
|
39
|
+
cfg2 = ConfigSync(tmp_config)
|
|
40
|
+
assert cfg2.get("server.port") == 9090
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_merge_configs():
|
|
44
|
+
base = {"server": {"host": "localhost", "port": 8080}, "debug": False}
|
|
45
|
+
override = {"server": {"port": 9090}, "debug": True}
|
|
46
|
+
merged = merge_configs(base, override)
|
|
47
|
+
assert merged["server"]["host"] == "localhost"
|
|
48
|
+
assert merged["server"]["port"] == 9090
|
|
49
|
+
assert merged["debug"] is True
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_interpolate_env(tmp_config):
|
|
53
|
+
os.environ["DB_URL"] = "postgresql://localhost/mydb"
|
|
54
|
+
raw = load_config(tmp_config)
|
|
55
|
+
resolved = interpolate_env(raw)
|
|
56
|
+
assert resolved["database"]["url"] == "postgresql://localhost/mydb"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_validate_schema_valid(tmp_config):
|
|
60
|
+
config = {"host": "localhost", "port": 8080, "debug": True}
|
|
61
|
+
schema = {
|
|
62
|
+
"host": {"type": "str", "required": True},
|
|
63
|
+
"port": {"type": "int", "required": True},
|
|
64
|
+
"debug": {"type": "bool", "required": False}
|
|
65
|
+
}
|
|
66
|
+
errors = validate_schema(config, schema)
|
|
67
|
+
assert errors == []
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_validate_schema_missing_required():
|
|
71
|
+
config = {"port": 8080}
|
|
72
|
+
schema = {"host": {"type": "str", "required": True}}
|
|
73
|
+
errors = validate_schema(config, schema)
|
|
74
|
+
assert len(errors) == 1
|
|
75
|
+
assert "host" in errors[0]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_validate_schema_wrong_type():
|
|
79
|
+
config = {"port": "not-an-int"}
|
|
80
|
+
schema = {"port": {"type": "int", "required": True}}
|
|
81
|
+
errors = validate_schema(config, schema)
|
|
82
|
+
assert len(errors) == 1
|