Skip to content

Commit e0c73fb

Browse files
authored
Configuration fixes (#8)
* fix: prevent flags from overwriting env with their default value when they are not set * fix: merge user defined configuration with default configuration * fix: remove dead code
1 parent 40346ea commit e0c73fb

File tree

1 file changed

+27
-29
lines changed

1 file changed

+27
-29
lines changed

config.py

+27-29
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import os
55
import argparse
66
import sys
7-
from typing import NamedTuple, Dict, Any
8-
from types import SimpleNamespace
7+
from typing import NamedTuple
98

109
# Temporary logging configuration
1110
coloredlogs.install(level="WARN")
@@ -69,29 +68,6 @@ class Config(NamedTuple):
6968
logLevel: LogLevelConfig
7069
traefik: TraefikConfig
7170

72-
def dict_to_simplenamespace(dict_obj):
73-
"""
74-
Recursively converts a nested dictionary into a SimpleNamespace object.
75-
76-
Args:
77-
dict_obj (dict): The input dictionary to be converted.
78-
79-
Returns:
80-
SimpleNamespace: The resulting SimpleNamespace object.
81-
"""
82-
logging.info("Converting dictionary to SimpleNamespace")
83-
logging.debug(f"Input dictionary: {dict_obj}")
84-
if isinstance(dict_obj, dict):
85-
for key, value in dict_obj.items():
86-
logging.debug(f"Processing key: {key}")
87-
dict_obj[key] = dict_to_simplenamespace(value)
88-
elif isinstance(dict_obj, list):
89-
logging.debug(f"Processing list with {len(dict_obj)} items")
90-
return [dict_to_simplenamespace(item) for item in dict_obj]
91-
result = SimpleNamespace(**dict_obj) if isinstance(dict_obj, dict) else dict_obj
92-
logging.debug(f"Resulting SimpleNamespace or value: {result}")
93-
return result
94-
9571
def flatten_keys(d, parent_key='', sep='.'):
9672
"""
9773
Flattens a nested dictionary into a flat dictionary with concatenated keys.
@@ -113,6 +89,28 @@ def flatten_keys(d, parent_key='', sep='.'):
11389
items.append((new_key, v))
11490
return dict(items)
11591

92+
def merge_dicts(lhs, rhs):
93+
"""
94+
Recursively merges two dictionaries.
95+
If there's a conflict, values from rhs will overwrite those from lhs.
96+
97+
Args:
98+
lhs (dict): Left hand side dictionary to merge.
99+
rhs (dict): Right hand side dictionary to merge.
100+
101+
Returns:
102+
merged: The merged dictionary.
103+
"""
104+
merged = lhs.copy()
105+
106+
for key, value in rhs.items():
107+
if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
108+
merged[key] = merge_dicts(merged[key], value)
109+
else:
110+
merged[key] = value
111+
112+
return merged
113+
116114
def parse_args(config):
117115
"""
118116
Parses command line arguments based on the configuration.
@@ -132,7 +130,6 @@ def parse_args(config):
132130
cli_key = key.replace('..', '.').lower() # Ensure CLI arguments are lowercase
133131
parser.add_argument(f'--{cli_key}',
134132
type=type(value),
135-
default=value,
136133
help=f'Specify the {cli_key} value')
137134
args, unknown = parser.parse_known_args()
138135

@@ -150,14 +147,15 @@ def load_config() -> Config:
150147
"""
151148
logging.info("Loading configuration from file")
152149
with open('config.yaml', "r") as file:
153-
config_data = yaml.safe_load(file)
154-
logging.debug("Loaded initial configuration: %s", config_data)
150+
default_config = yaml.safe_load(file)
151+
logging.debug("Loaded default configuration: %s", default_config)
155152

156-
args = parse_args(config_data)
153+
args = parse_args(default_config)
157154

158155
if args.config:
159156
with open(args.config, "r") as file:
160157
config_data = yaml.safe_load(file)
158+
config_data = merge_dicts(default_config, config_data)
161159
logging.info("Loaded configuration from file: %s", config_data)
162160

163161
apply_overrides_from_env_and_cli(config_data, args)

0 commit comments

Comments
 (0)