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.
@@ -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
+ 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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