| Name | etcd-dynamic-config JSON |
| Version |
0.1.0
JSON |
| download |
| home_page | None |
| Summary | A Python library for managing etcd-based configurations with caching and real-time updates |
| upload_time | 2025-09-10 16:52:49 |
| maintainer | None |
| docs_url | None |
| author | None |
| requires_python | >=3.8 |
| license | MIT |
| keywords |
etcd
configuration
cache
watcher
async
|
| VCS |
 |
| bugtrack_url |
|
| requirements |
No requirements were recorded.
|
| Travis-CI |
No Travis.
|
| coveralls test coverage |
No coveralls.
|
# Etcd Dynamic Config
[](https://pypi.org/project/etcd-dynamic-config/)
[](https://pypi.org/project/etcd-dynamic-config/)
[](https://github.com/ton5169/etcd-dynamic-config/blob/main/LICENSE)
A robust Python library for managing etcd-based configurations with caching, real-time updates, and graceful fallbacks.
## Features
- ๐ **High Performance**: In-memory caching for fast configuration access
- ๐ **Real-time Updates**: Automatic watching for configuration changes
- ๐ก๏ธ **Resilient**: Graceful fallbacks to local environment variables
- ๐ **Secure**: Support for TLS and authentication
- ๐งต **Thread-safe**: Safe concurrent access to configuration data
- ๐ **Observable**: Structured logging and monitoring support
- ๐ฏ **Type-safe**: Built-in type coercion and validation
## Installation
```bash
pip install etcd-dynamic-config
```
For development with extra tools:
```bash
pip install etcd-dynamic-config[dev]
```
## Quick Start
### Basic Usage
```python
import asyncio
from etcd_dynamic_config import etcd_config
async def main():
# Start the configuration manager
success = await etcd_config.start()
if success:
# Get all configurations
configs = await etcd_config.get_all_configs()
print(f"Database URL: {configs.get('postgres_dsn')}")
# Access specific config values
api_url = configs.get('categorization_api_url')
print(f"API URL: {api_url}")
# Clean shutdown
await etcd_config.stop()
# Run the example
asyncio.run(main())
```
### Environment Variables
Set these environment variables to configure etcd connection:
```bash
# Etcd connection settings
export EtcdSettings__HostName="http://localhost:2379"
export EtcdSettings__UserName="your-username"
export EtcdSettings__Password="your-password"
export EtcdSettings__RootKey="/APPS/ControlUnit"
# Optional: Use local environment variables instead of etcd
export USE_LOCAL_CONFIG="false"
# Optional: TLS settings
export EtcdSettings__CaCertPath="/path/to/ca-cert.pem"
```
### Local Development
For local development, set `USE_LOCAL_CONFIG=true` and define configurations as environment variables:
```bash
export USE_LOCAL_CONFIG="true"
export CATEGORIZATION_API_URL="http://localhost:8000"
export POSTGRES_DSN="postgresql://user:pass@localhost:5432/db"
export LOG_LEVEL="DEBUG"
```
## Advanced Usage
### Custom Configuration Keys
#### Using Built-in ControlUnit Client
```python
from etcd_dynamic_config import EtcdClient
# Use the built-in ControlUnit client (backward compatible)
client = EtcdClient()
# Get specific configuration values
key_map = client.get_control_unit_key_map()
print("Available config keys:", list(key_map.values()))
# Direct key access
values = client.get_values_by_keys(["/APPS/ControlUnit/LogLevel"])
print("Log level:", values.get("/APPS/ControlUnit/LogLevel"))
```
#### Creating Custom Clients
```python
from etcd_dynamic_config import BaseEtcdClient
class MyAppEtcdClient(BaseEtcdClient):
def get_config_prefix(self) -> str:
return "/apps/myapp/prod"
def _build_etcd_key_map(self) -> Dict[str, str]:
base = self.get_config_prefix()
return {
f"{base}/DatabaseUrl": "database_url",
f"{base}/RedisUrl": "redis_url",
f"{base}/ApiKey": "api_key",
}
def _build_env_var_map(self) -> Dict[str, str]:
return {
"database_url": "MYAPP_DATABASE_URL",
"redis_url": "MYAPP_REDIS_URL",
"api_key": "MYAPP_API_KEY",
}
# Use custom client
client = MyAppEtcdClient(use_local_config=True)
config = client.get_config()
print("Database URL:", config.get("database_url"))
```
### Async Configuration Manager
```python
import asyncio
from etcd_dynamic_config import EtcdConfig
async def config_worker():
config_manager = EtcdConfig()
# Start with watching
await config_manager.start()
try:
while True:
configs = await config_manager.get_all_configs()
# Your application logic here
api_token = configs.get('categorization_api_token')
if api_token:
print(f"Using API token: {api_token[:10]}...")
await asyncio.sleep(60) # Check every minute
finally:
await config_manager.stop()
asyncio.run(config_worker())
```
### Error Handling
```python
import asyncio
from etcd_dynamic_config import etcd_config
async def robust_config_access():
try:
await etcd_config.start()
configs = await etcd_config.get_all_configs()
# Safe access with defaults
timeout = configs.get('ai_http_timeout_seconds', 30.0)
max_conn = configs.get('ai_http_max_connections', 10)
print(f"Timeout: {timeout}s, Max connections: {max_conn}")
except Exception as e:
print(f"Configuration error: {e}")
# Fallback to hardcoded defaults
timeout = 30.0
max_conn = 10
finally:
await etcd_config.stop()
```
## Configuration Schema
The library doesn't impose any specific configuration schema - **you define your own keys!**
### Defining Your Configuration Schema
```python
from etcd_dynamic_config import BaseEtcdClient
class MyAppClient(BaseEtcdClient):
def get_config_prefix(self) -> str:
return "/myapp/production"
def _build_etcd_key_map(self) -> Dict[str, str]:
base = self.get_config_prefix()
return {
# Your custom etcd keys -> internal names
f"{base}/Database/Host": "db_host",
f"{base}/Database/Port": "db_port",
f"{base}/Database/Name": "db_name",
f"{base}/Cache/RedisUrl": "redis_url",
f"{base}/API/SecretKey": "api_secret",
f"{base}/Features/EnableCache": "enable_cache",
f"{base}/Monitoring/LogLevel": "log_level",
}
def _build_env_var_map(self) -> Dict[str, str]:
return {
# Internal names -> environment variables
"db_host": "MYAPP_DB_HOST",
"db_port": "MYAPP_DB_PORT",
"db_name": "MYAPP_DB_NAME",
"redis_url": "MYAPP_REDIS_URL",
"api_secret": "MYAPP_API_SECRET",
"enable_cache": "MYAPP_ENABLE_CACHE",
"log_level": "MYAPP_LOG_LEVEL",
}
```
### Custom Type Coercion
```python
def _coerce_config_value(self, internal_name: str, value):
"""Define your own type coercion rules."""
if internal_name == "db_port":
return int(value) if value else 5432
elif internal_name == "enable_cache":
return str(value).lower() in ("1", "true", "yes", "on")
elif internal_name == "api_secret":
# Don't log secrets in plain text
return str(value) if value else ""
# Use default coercion for other values
return super()._coerce_config_value(internal_name, value)
```
### Your Configuration Documentation
Create documentation for **your** configuration keys:
| Your Etcd Key | Environment Variable | Type | Default | Description |
| ---------------------------------------- | -------------------- | ---- | --------- | --------------------- |
| `/myapp/production/Database/Host` | `MYAPP_DB_HOST` | str | localhost | Database host |
| `/myapp/production/Database/Port` | `MYAPP_DB_PORT` | int | 5432 | Database port |
| `/myapp/production/Cache/RedisUrl` | `MYAPP_REDIS_URL` | str | - | Redis connection URL |
| `/myapp/production/Features/EnableCache` | `MYAPP_ENABLE_CACHE` | bool | false | Enable caching |
| `/myapp/production/Monitoring/LogLevel` | `MYAPP_LOG_LEVEL` | str | INFO | Application log level |
See [examples/schema_documentation_example.py](examples/schema_documentation_example.py) for a complete example of documenting and implementing a custom configuration schema.
### Built-in ControlUnit Client
For backward compatibility, the library includes a pre-configured client for ControlUnit applications:
```python
from etcd_dynamic_config import EtcdClient, etcd_client
# Uses the ControlUnit schema automatically
client = EtcdClient()
config = client.get_config()
# Access ControlUnit-specific keys
api_url = config.get("categorization_api_url")
db_dsn = config.get("postgres_dsn")
```
The ControlUnit client handles these keys automatically (see [ControlUnitEtcdClient](https://github.com/ton5169/etcd-dynamic-config/blob/main/etcd_dynamic_config/core/control_unit.py) for details).
## Architecture
### Components
1. **EtcdClient**: Low-level etcd operations
- Connection management
- Authentication and TLS
- Key-value operations
- Watching capabilities
2. **EtcdConfig**: High-level configuration management
- Caching layer
- Type coercion
- Real-time updates
- Health monitoring
### Thread Safety
All operations are thread-safe:
- Configuration cache uses `threading.RLock()`
- Async operations properly handle concurrency
- Watcher callbacks are serialized
### Error Recovery
The library implements several recovery mechanisms:
- Automatic reconnection on auth failures
- Watcher restart on inactivity
- Fallback to local environment variables
- Graceful degradation on etcd unavailability
## Development
### Setup
```bash
# Clone the repository
git clone https://github.com/ton5169/etcd-dynamic-config.git
cd etcd-dynamic-config
# Install development dependencies
pip install -e .[dev]
# Run tests
pytest
# Format code
black etcd_dynamic_config/
isort etcd_dynamic_config/
# Type checking
mypy etcd_dynamic_config/
```
### Testing
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=etcd_dynamic_config --cov-report=html
# Run specific test file
pytest tests/test_client.py
# Run integration tests
pytest -m integration
```
### Building Documentation
```bash
# Install docs dependencies
pip install -e .[docs]
# Build docs
cd docs
make html
```
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Ensure all tests pass
6. Submit a pull request
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Extensibility & Customization
### Creating Custom Clients
The library provides `BaseEtcdClient` for creating custom implementations:
```python
from etcd_dynamic_config import BaseEtcdClient
class MyServiceClient(BaseEtcdClient):
def get_config_prefix(self) -> str:
return "/services/my-service"
def _build_etcd_key_map(self) -> Dict[str, str]:
base = self.get_config_prefix()
return {
f"{base}/DatabaseUrl": "database_url",
f"{base}/RedisUrl": "redis_url",
f"{base}/ApiKey": "api_key",
f"{base}/MaxWorkers": "max_workers",
}
def _build_env_var_map(self) -> Dict[str, str]:
return {
"database_url": "MYAPP_DATABASE_URL",
"redis_url": "MYAPP_REDIS_URL",
"api_key": "MYAPP_API_KEY",
"max_workers": "MYAPP_MAX_WORKERS",
}
def _coerce_config_value(self, name: str, value):
if name == "max_workers":
return int(value) if value else 4
return super()._coerce_config_value(name, value)
# Use your custom client
client = MyServiceClient(use_local_config=True)
config = client.get_config()
```
### Available Classes
- **BaseEtcdClient**: Abstract base for custom implementations
- **ControlUnitEtcdClient**: Pre-configured for ControlUnit (default)
- **EtcdClient**: Alias for ControlUnitEtcdClient (backward compatibility)
- **EtcdConfig**: High-level configuration manager
### Key Benefits
- **๐ฏ Universal**: Works with any etcd key structure
- **๐ง Customizable**: Easy to adapt for different applications
- **๐ Backward Compatible**: Existing code continues to work
- **๐๏ธ Extensible**: Clean architecture for future enhancements
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for version history.
Raw data
{
"_id": null,
"home_page": null,
"name": "etcd-dynamic-config",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "Anton Irshenko <a_irshenko@example.com>",
"keywords": "etcd, configuration, cache, watcher, async",
"author": null,
"author_email": "Anton Irshenko <a_irshenko@example.com>",
"download_url": "https://files.pythonhosted.org/packages/4f/9d/6b1e5ef6161143615412e9035707b8424d004645881fea69ecc9858e56e9/etcd_dynamic_config-0.1.0.tar.gz",
"platform": null,
"description": "# Etcd Dynamic Config\n\n[](https://pypi.org/project/etcd-dynamic-config/)\n[](https://pypi.org/project/etcd-dynamic-config/)\n[](https://github.com/ton5169/etcd-dynamic-config/blob/main/LICENSE)\n\nA robust Python library for managing etcd-based configurations with caching, real-time updates, and graceful fallbacks.\n\n## Features\n\n- \ud83d\ude80 **High Performance**: In-memory caching for fast configuration access\n- \ud83d\udd04 **Real-time Updates**: Automatic watching for configuration changes\n- \ud83d\udee1\ufe0f **Resilient**: Graceful fallbacks to local environment variables\n- \ud83d\udd12 **Secure**: Support for TLS and authentication\n- \ud83e\uddf5 **Thread-safe**: Safe concurrent access to configuration data\n- \ud83d\udcca **Observable**: Structured logging and monitoring support\n- \ud83c\udfaf **Type-safe**: Built-in type coercion and validation\n\n## Installation\n\n```bash\npip install etcd-dynamic-config\n```\n\nFor development with extra tools:\n\n```bash\npip install etcd-dynamic-config[dev]\n```\n\n## Quick Start\n\n### Basic Usage\n\n```python\nimport asyncio\nfrom etcd_dynamic_config import etcd_config\n\nasync def main():\n # Start the configuration manager\n success = await etcd_config.start()\n if success:\n # Get all configurations\n configs = await etcd_config.get_all_configs()\n print(f\"Database URL: {configs.get('postgres_dsn')}\")\n\n # Access specific config values\n api_url = configs.get('categorization_api_url')\n print(f\"API URL: {api_url}\")\n\n # Clean shutdown\n await etcd_config.stop()\n\n# Run the example\nasyncio.run(main())\n```\n\n### Environment Variables\n\nSet these environment variables to configure etcd connection:\n\n```bash\n# Etcd connection settings\nexport EtcdSettings__HostName=\"http://localhost:2379\"\nexport EtcdSettings__UserName=\"your-username\"\nexport EtcdSettings__Password=\"your-password\"\nexport EtcdSettings__RootKey=\"/APPS/ControlUnit\"\n\n# Optional: Use local environment variables instead of etcd\nexport USE_LOCAL_CONFIG=\"false\"\n\n# Optional: TLS settings\nexport EtcdSettings__CaCertPath=\"/path/to/ca-cert.pem\"\n```\n\n### Local Development\n\nFor local development, set `USE_LOCAL_CONFIG=true` and define configurations as environment variables:\n\n```bash\nexport USE_LOCAL_CONFIG=\"true\"\nexport CATEGORIZATION_API_URL=\"http://localhost:8000\"\nexport POSTGRES_DSN=\"postgresql://user:pass@localhost:5432/db\"\nexport LOG_LEVEL=\"DEBUG\"\n```\n\n## Advanced Usage\n\n### Custom Configuration Keys\n\n#### Using Built-in ControlUnit Client\n\n```python\nfrom etcd_dynamic_config import EtcdClient\n\n# Use the built-in ControlUnit client (backward compatible)\nclient = EtcdClient()\n\n# Get specific configuration values\nkey_map = client.get_control_unit_key_map()\nprint(\"Available config keys:\", list(key_map.values()))\n\n# Direct key access\nvalues = client.get_values_by_keys([\"/APPS/ControlUnit/LogLevel\"])\nprint(\"Log level:\", values.get(\"/APPS/ControlUnit/LogLevel\"))\n```\n\n#### Creating Custom Clients\n\n```python\nfrom etcd_dynamic_config import BaseEtcdClient\n\nclass MyAppEtcdClient(BaseEtcdClient):\n def get_config_prefix(self) -> str:\n return \"/apps/myapp/prod\"\n\n def _build_etcd_key_map(self) -> Dict[str, str]:\n base = self.get_config_prefix()\n return {\n f\"{base}/DatabaseUrl\": \"database_url\",\n f\"{base}/RedisUrl\": \"redis_url\",\n f\"{base}/ApiKey\": \"api_key\",\n }\n\n def _build_env_var_map(self) -> Dict[str, str]:\n return {\n \"database_url\": \"MYAPP_DATABASE_URL\",\n \"redis_url\": \"MYAPP_REDIS_URL\",\n \"api_key\": \"MYAPP_API_KEY\",\n }\n\n# Use custom client\nclient = MyAppEtcdClient(use_local_config=True)\nconfig = client.get_config()\nprint(\"Database URL:\", config.get(\"database_url\"))\n```\n\n### Async Configuration Manager\n\n```python\nimport asyncio\nfrom etcd_dynamic_config import EtcdConfig\n\nasync def config_worker():\n config_manager = EtcdConfig()\n\n # Start with watching\n await config_manager.start()\n\n try:\n while True:\n configs = await config_manager.get_all_configs()\n\n # Your application logic here\n api_token = configs.get('categorization_api_token')\n if api_token:\n print(f\"Using API token: {api_token[:10]}...\")\n\n await asyncio.sleep(60) # Check every minute\n\n finally:\n await config_manager.stop()\n\nasyncio.run(config_worker())\n```\n\n### Error Handling\n\n```python\nimport asyncio\nfrom etcd_dynamic_config import etcd_config\n\nasync def robust_config_access():\n try:\n await etcd_config.start()\n\n configs = await etcd_config.get_all_configs()\n\n # Safe access with defaults\n timeout = configs.get('ai_http_timeout_seconds', 30.0)\n max_conn = configs.get('ai_http_max_connections', 10)\n\n print(f\"Timeout: {timeout}s, Max connections: {max_conn}\")\n\n except Exception as e:\n print(f\"Configuration error: {e}\")\n # Fallback to hardcoded defaults\n timeout = 30.0\n max_conn = 10\n\n finally:\n await etcd_config.stop()\n```\n\n## Configuration Schema\n\nThe library doesn't impose any specific configuration schema - **you define your own keys!**\n\n### Defining Your Configuration Schema\n\n```python\nfrom etcd_dynamic_config import BaseEtcdClient\n\nclass MyAppClient(BaseEtcdClient):\n def get_config_prefix(self) -> str:\n return \"/myapp/production\"\n\n def _build_etcd_key_map(self) -> Dict[str, str]:\n base = self.get_config_prefix()\n return {\n # Your custom etcd keys -> internal names\n f\"{base}/Database/Host\": \"db_host\",\n f\"{base}/Database/Port\": \"db_port\",\n f\"{base}/Database/Name\": \"db_name\",\n f\"{base}/Cache/RedisUrl\": \"redis_url\",\n f\"{base}/API/SecretKey\": \"api_secret\",\n f\"{base}/Features/EnableCache\": \"enable_cache\",\n f\"{base}/Monitoring/LogLevel\": \"log_level\",\n }\n\n def _build_env_var_map(self) -> Dict[str, str]:\n return {\n # Internal names -> environment variables\n \"db_host\": \"MYAPP_DB_HOST\",\n \"db_port\": \"MYAPP_DB_PORT\",\n \"db_name\": \"MYAPP_DB_NAME\",\n \"redis_url\": \"MYAPP_REDIS_URL\",\n \"api_secret\": \"MYAPP_API_SECRET\",\n \"enable_cache\": \"MYAPP_ENABLE_CACHE\",\n \"log_level\": \"MYAPP_LOG_LEVEL\",\n }\n```\n\n### Custom Type Coercion\n\n```python\ndef _coerce_config_value(self, internal_name: str, value):\n \"\"\"Define your own type coercion rules.\"\"\"\n if internal_name == \"db_port\":\n return int(value) if value else 5432\n elif internal_name == \"enable_cache\":\n return str(value).lower() in (\"1\", \"true\", \"yes\", \"on\")\n elif internal_name == \"api_secret\":\n # Don't log secrets in plain text\n return str(value) if value else \"\"\n\n # Use default coercion for other values\n return super()._coerce_config_value(internal_name, value)\n```\n\n### Your Configuration Documentation\n\nCreate documentation for **your** configuration keys:\n\n| Your Etcd Key | Environment Variable | Type | Default | Description |\n| ---------------------------------------- | -------------------- | ---- | --------- | --------------------- |\n| `/myapp/production/Database/Host` | `MYAPP_DB_HOST` | str | localhost | Database host |\n| `/myapp/production/Database/Port` | `MYAPP_DB_PORT` | int | 5432 | Database port |\n| `/myapp/production/Cache/RedisUrl` | `MYAPP_REDIS_URL` | str | - | Redis connection URL |\n| `/myapp/production/Features/EnableCache` | `MYAPP_ENABLE_CACHE` | bool | false | Enable caching |\n| `/myapp/production/Monitoring/LogLevel` | `MYAPP_LOG_LEVEL` | str | INFO | Application log level |\n\nSee [examples/schema_documentation_example.py](examples/schema_documentation_example.py) for a complete example of documenting and implementing a custom configuration schema.\n\n### Built-in ControlUnit Client\n\nFor backward compatibility, the library includes a pre-configured client for ControlUnit applications:\n\n```python\nfrom etcd_dynamic_config import EtcdClient, etcd_client\n\n# Uses the ControlUnit schema automatically\nclient = EtcdClient()\nconfig = client.get_config()\n\n# Access ControlUnit-specific keys\napi_url = config.get(\"categorization_api_url\")\ndb_dsn = config.get(\"postgres_dsn\")\n```\n\nThe ControlUnit client handles these keys automatically (see [ControlUnitEtcdClient](https://github.com/ton5169/etcd-dynamic-config/blob/main/etcd_dynamic_config/core/control_unit.py) for details).\n\n## Architecture\n\n### Components\n\n1. **EtcdClient**: Low-level etcd operations\n\n - Connection management\n - Authentication and TLS\n - Key-value operations\n - Watching capabilities\n\n2. **EtcdConfig**: High-level configuration management\n - Caching layer\n - Type coercion\n - Real-time updates\n - Health monitoring\n\n### Thread Safety\n\nAll operations are thread-safe:\n\n- Configuration cache uses `threading.RLock()`\n- Async operations properly handle concurrency\n- Watcher callbacks are serialized\n\n### Error Recovery\n\nThe library implements several recovery mechanisms:\n\n- Automatic reconnection on auth failures\n- Watcher restart on inactivity\n- Fallback to local environment variables\n- Graceful degradation on etcd unavailability\n\n## Development\n\n### Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/ton5169/etcd-dynamic-config.git\ncd etcd-dynamic-config\n\n# Install development dependencies\npip install -e .[dev]\n\n# Run tests\npytest\n\n# Format code\nblack etcd_dynamic_config/\nisort etcd_dynamic_config/\n\n# Type checking\nmypy etcd_dynamic_config/\n```\n\n### Testing\n\n```bash\n# Run all tests\npytest\n\n# Run with coverage\npytest --cov=etcd_dynamic_config --cov-report=html\n\n# Run specific test file\npytest tests/test_client.py\n\n# Run integration tests\npytest -m integration\n```\n\n### Building Documentation\n\n```bash\n# Install docs dependencies\npip install -e .[docs]\n\n# Build docs\ncd docs\nmake html\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Add tests for new functionality\n5. Ensure all tests pass\n6. Submit a pull request\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n## Extensibility & Customization\n\n### Creating Custom Clients\n\nThe library provides `BaseEtcdClient` for creating custom implementations:\n\n```python\nfrom etcd_dynamic_config import BaseEtcdClient\n\nclass MyServiceClient(BaseEtcdClient):\n def get_config_prefix(self) -> str:\n return \"/services/my-service\"\n\n def _build_etcd_key_map(self) -> Dict[str, str]:\n base = self.get_config_prefix()\n return {\n f\"{base}/DatabaseUrl\": \"database_url\",\n f\"{base}/RedisUrl\": \"redis_url\",\n f\"{base}/ApiKey\": \"api_key\",\n f\"{base}/MaxWorkers\": \"max_workers\",\n }\n\n def _build_env_var_map(self) -> Dict[str, str]:\n return {\n \"database_url\": \"MYAPP_DATABASE_URL\",\n \"redis_url\": \"MYAPP_REDIS_URL\",\n \"api_key\": \"MYAPP_API_KEY\",\n \"max_workers\": \"MYAPP_MAX_WORKERS\",\n }\n\n def _coerce_config_value(self, name: str, value):\n if name == \"max_workers\":\n return int(value) if value else 4\n return super()._coerce_config_value(name, value)\n\n# Use your custom client\nclient = MyServiceClient(use_local_config=True)\nconfig = client.get_config()\n```\n\n### Available Classes\n\n- **BaseEtcdClient**: Abstract base for custom implementations\n- **ControlUnitEtcdClient**: Pre-configured for ControlUnit (default)\n- **EtcdClient**: Alias for ControlUnitEtcdClient (backward compatibility)\n- **EtcdConfig**: High-level configuration manager\n\n### Key Benefits\n\n- **\ud83c\udfaf Universal**: Works with any etcd key structure\n- **\ud83d\udd27 Customizable**: Easy to adapt for different applications\n- **\ud83d\udd04 Backward Compatible**: Existing code continues to work\n- **\ud83c\udfd7\ufe0f Extensible**: Clean architecture for future enhancements\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for version history.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A Python library for managing etcd-based configurations with caching and real-time updates",
"version": "0.1.0",
"project_urls": {
"Changelog": "https://github.com/ton5169/etcd-dynamic-config/blob/main/CHANGELOG.md",
"Documentation": "https://github.com/ton5169/etcd-dynamic-config#readme",
"Homepage": "https://github.com/ton5169/etcd-dynamic-config",
"Issues": "https://github.com/ton5169/etcd-dynamic-config/issues",
"Repository": "https://github.com/ton5169/etcd-dynamic-config"
},
"split_keywords": [
"etcd",
" configuration",
" cache",
" watcher",
" async"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "9b1db4e483b6e3213d34ab48fc480181c4219f25b5238f2436c4f499e2a25833",
"md5": "28cf77b1921010371f2a35a19caee5be",
"sha256": "e746d18d2b687c29e9ffbd2df89fbd6008de235dd3389d7c978097bdc0ed11b2"
},
"downloads": -1,
"filename": "etcd_dynamic_config-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "28cf77b1921010371f2a35a19caee5be",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 20418,
"upload_time": "2025-09-10T16:52:48",
"upload_time_iso_8601": "2025-09-10T16:52:48.278196Z",
"url": "https://files.pythonhosted.org/packages/9b/1d/b4e483b6e3213d34ab48fc480181c4219f25b5238f2436c4f499e2a25833/etcd_dynamic_config-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "4f9d6b1e5ef6161143615412e9035707b8424d004645881fea69ecc9858e56e9",
"md5": "4e4eb03383de94c063167bd6c106291d",
"sha256": "1ea2931bf44da7baa024df05de1d81b1282988ab740cce213a2639f36d04b96a"
},
"downloads": -1,
"filename": "etcd_dynamic_config-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "4e4eb03383de94c063167bd6c106291d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 24415,
"upload_time": "2025-09-10T16:52:49",
"upload_time_iso_8601": "2025-09-10T16:52:49.537908Z",
"url": "https://files.pythonhosted.org/packages/4f/9d/6b1e5ef6161143615412e9035707b8424d004645881fea69ecc9858e56e9/etcd_dynamic_config-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-10 16:52:49",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ton5169",
"github_project": "etcd-dynamic-config",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "etcd-dynamic-config"
}