feat(python-naive): Add concept of non-overridable globals
This commit is contained in:
parent
7e971bc330
commit
e265dc3853
@ -17,17 +17,20 @@ class CONST(object):
|
||||
CFG_THIS_FILE_DIRNAME = os.path.dirname(__file__)
|
||||
CFG_DEFAULT_FILENAME = "config.ini"
|
||||
CFG_DEFAULT_ABS_PATH = os.path.join(CFG_THIS_FILE_DIRNAME, CFG_DEFAULT_FILENAME)
|
||||
# Values you don't have to set, these are their internal defaults
|
||||
# Values you don't have to set, these are their internal defaults. You may optionally add a key 'is_global' equal
|
||||
# to either True or False. By default if left off it'll be assumed False. Script will treat values where
|
||||
# 'is_global' equals True as not being overridable in a '[section]'. It's a setting that only makes sense in a
|
||||
# global context for the entire script.
|
||||
CFG_KNOWN_DEFAULTS = [
|
||||
{"key": "self_name", "value": "rich-and-config"},
|
||||
{"key": "tmp_base_dir", "value": os.path.join(CFG_THIS_FILE_DIRNAME, "data/tmp/%(self_name)s")},
|
||||
{"key": "state_base_dir", "value": os.path.join(CFG_THIS_FILE_DIRNAME, "data/var/lib/%(self_name)s")},
|
||||
{"key": "state_files_dir", "value": "%(state_base_dir)s/state"},
|
||||
{"key": "state_file_retention", "value": "50"},
|
||||
{"key": "state_file_name_prefix", "value": "state-"},
|
||||
{"key": "state_file_name_suffix", "value": ".log"},
|
||||
{"key": "rich_and_config_some_option", "value": "http://localhost:8000/api/query"},
|
||||
{"key": "another_option", "value": "first"}
|
||||
{"key": "state_files_dir", "value": "%(state_base_dir)s/state", "is_global": False},
|
||||
{"key": "state_file_retention", "value": "50", "is_global": False},
|
||||
{"key": "state_file_name_prefix", "value": "state-", "is_global": False},
|
||||
{"key": "state_file_name_suffix", "value": ".log", "is_global": False},
|
||||
{"key": "rich_and_config_some_option", "value": "http://localhost:8000/api/query", "is_global": True},
|
||||
{"key": "another_option", "value": "first", "is_global": True}
|
||||
]
|
||||
# In all sections other than 'default' the following settings are known and accepted. We silently ignore other
|
||||
# settings. We use 'is_mandatory' to determine if we have to raise errors on missing settings.
|
||||
@ -76,6 +79,7 @@ class ConfigParser(
|
||||
|
||||
ini_defaults = []
|
||||
internal_defaults = {default["key"]: default["value"] for default in CONST.CFG_KNOWN_DEFAULTS}
|
||||
internal_globals = [default["key"] for default in CONST.CFG_KNOWN_DEFAULTS if default["is_global"]]
|
||||
config = ConfigParser(defaults=internal_defaults)
|
||||
config.read(CONST.CFG_DEFAULT_ABS_PATH)
|
||||
|
||||
@ -93,14 +97,19 @@ def validate_default_section(
|
||||
sys.exit(1)
|
||||
if config.defaults():
|
||||
log.debug(f"Symbol legend:\n"
|
||||
f"* Global default from section '[{config_obj.default_section}]'\n"
|
||||
f"* Default from section '[{config_obj.default_section}]'\n"
|
||||
f": Global option from '[{config_obj.default_section}]', can not be overridden in local sections\n"
|
||||
f"~ Local option, doesn't exist in '[{config_obj.default_section}]'\n"
|
||||
f"+ Local override of a value from '[{config_obj.default_section}]'\n"
|
||||
f"= Local override, same value as in '[{config_obj.default_section}]'")
|
||||
f"= Local override, same value as in '[{config_obj.default_section}]'\n"
|
||||
f"# Local attempt at overriding a global, will be ignored")
|
||||
log.debug(print_section_header(config_obj.default_section))
|
||||
for default in config_obj.defaults():
|
||||
ini_defaults.append({default: config_obj[config_obj.default_section][default]})
|
||||
log.debug(f"* {default} = {config_obj[config_obj.default_section][default]}")
|
||||
if default in internal_globals:
|
||||
log.debug(f": {default} = {config_obj[config_obj.default_section][default]}")
|
||||
else:
|
||||
log.debug(f"* {default} = {config_obj[config_obj.default_section][default]}")
|
||||
else:
|
||||
log.debug(f"No defaults defined")
|
||||
|
||||
@ -120,6 +129,11 @@ def is_default(
|
||||
return any(config_key in ini_default for ini_default in ini_defaults)
|
||||
|
||||
|
||||
def is_global(
|
||||
config_key: str) -> bool:
|
||||
return config_key in internal_globals
|
||||
|
||||
|
||||
def is_same_as_default(
|
||||
config_kv_pair: dict) -> bool:
|
||||
return config_kv_pair in ini_defaults
|
||||
@ -136,11 +150,17 @@ def validate_config_sections(
|
||||
else:
|
||||
for key in config_obj.options(this_section, no_defaults=True):
|
||||
kv_prefix = "~"
|
||||
if is_default(key):
|
||||
remove_from_section = False
|
||||
if is_global(key):
|
||||
kv_prefix = "#"
|
||||
remove_from_section = True
|
||||
elif is_default(key):
|
||||
kv_prefix = "+"
|
||||
if is_same_as_default({key: config_obj[this_section][key]}):
|
||||
kv_prefix = "="
|
||||
log.debug(f"{kv_prefix} {key} = {config_obj[this_section][key]}")
|
||||
if remove_from_section:
|
||||
config_obj.remove_option(this_section, key)
|
||||
|
||||
|
||||
def an_important_function(
|
||||
@ -164,4 +184,4 @@ if __name__ == '__main__':
|
||||
log.debug(f"Iterating over config sections ...")
|
||||
for section in config.sections():
|
||||
log.debug(f"Processing section '[{section}]' ...")
|
||||
# ...config.ini.example
|
||||
# ...
|
||||
|
@ -26,17 +26,20 @@ class CONST(object):
|
||||
CFG_THIS_FILE_DIRNAME = os.path.dirname(__file__)
|
||||
CFG_DEFAULT_FILENAME = "config.ini"
|
||||
CFG_DEFAULT_ABS_PATH = os.path.join(CFG_THIS_FILE_DIRNAME, CFG_DEFAULT_FILENAME)
|
||||
# Values you don't have to set, these are their internal defaults
|
||||
# Values you don't have to set, these are their internal defaults. You may optionally add a key 'is_global' equal
|
||||
# to either True or False. By default if left off it'll be assumed False. Script will treat values where
|
||||
# 'is_global' equals True as not being overridable in a '[section]'. It's a setting that only makes sense in a
|
||||
# global context for the entire script.
|
||||
CFG_KNOWN_DEFAULTS = [
|
||||
{"key": "self_name", "value": "{{ cookiecutter.__project_slug }}"},
|
||||
{"key": "tmp_base_dir", "value": os.path.join(CFG_THIS_FILE_DIRNAME, "data/tmp/%(self_name)s")},
|
||||
{"key": "state_base_dir", "value": os.path.join(CFG_THIS_FILE_DIRNAME, "data/var/lib/%(self_name)s")},
|
||||
{"key": "state_files_dir", "value": "%(state_base_dir)s/state"},
|
||||
{"key": "state_file_retention", "value": "50"},
|
||||
{"key": "state_file_name_prefix", "value": "state-"},
|
||||
{"key": "state_file_name_suffix", "value": ".log"},
|
||||
{"key": "{{ cookiecutter.__project_slug_under }}_some_option", "value": "http://localhost:8000/api/query"},
|
||||
{"key": "another_option", "value": "first"}
|
||||
{"key": "state_files_dir", "value": "%(state_base_dir)s/state", "is_global": False},
|
||||
{"key": "state_file_retention", "value": "50", "is_global": False},
|
||||
{"key": "state_file_name_prefix", "value": "state-", "is_global": False},
|
||||
{"key": "state_file_name_suffix", "value": ".log", "is_global": False},
|
||||
{"key": "{{ cookiecutter.__project_slug_under }}_some_option", "value": "http://localhost:8000/api/query", "is_global": True},
|
||||
{"key": "another_option", "value": "first", "is_global": True}
|
||||
]
|
||||
# In all sections other than 'default' the following settings are known and accepted. We silently ignore other
|
||||
# settings. We use 'is_mandatory' to determine if we have to raise errors on missing settings.
|
||||
@ -89,6 +92,7 @@ class ConfigParser(
|
||||
|
||||
ini_defaults = []
|
||||
internal_defaults = {default["key"]: default["value"] for default in CONST.CFG_KNOWN_DEFAULTS}
|
||||
internal_globals = [default["key"] for default in CONST.CFG_KNOWN_DEFAULTS if default["is_global"]]
|
||||
config = ConfigParser(defaults=internal_defaults)
|
||||
config.read(CONST.CFG_DEFAULT_ABS_PATH)
|
||||
|
||||
@ -106,14 +110,19 @@ def validate_default_section(
|
||||
sys.exit(1)
|
||||
if config.defaults():
|
||||
{% if cookiecutter.rich_logging == "yes" -%}log.debug{%- else -%}print{%- endif %}(f"Symbol legend:\n"
|
||||
{% if cookiecutter.rich_logging == "yes" %} {% endif %}f"* Global default from section '[{config_obj.default_section}]'\n"
|
||||
{% if cookiecutter.rich_logging == "yes" %} {% endif %}f"* Default from section '[{config_obj.default_section}]'\n"
|
||||
{% if cookiecutter.rich_logging == "yes" %} {% endif %}f": Global option from '[{config_obj.default_section}]', can not be overridden in local sections\n"
|
||||
{% if cookiecutter.rich_logging == "yes" %} {% endif %}f"~ Local option, doesn't exist in '[{config_obj.default_section}]'\n"
|
||||
{% if cookiecutter.rich_logging == "yes" %} {% endif %}f"+ Local override of a value from '[{config_obj.default_section}]'\n"
|
||||
{% if cookiecutter.rich_logging == "yes" %} {% endif %}f"= Local override, same value as in '[{config_obj.default_section}]'")
|
||||
{% if cookiecutter.rich_logging == "yes" %} {% endif %}f"= Local override, same value as in '[{config_obj.default_section}]'\n"
|
||||
{% if cookiecutter.rich_logging == "yes" %} {% endif %}f"# Local attempt at overriding a global, will be ignored")
|
||||
{% if cookiecutter.rich_logging == "yes" -%}log.debug{%- else -%}print{%- endif %}(print_section_header(config_obj.default_section))
|
||||
for default in config_obj.defaults():
|
||||
ini_defaults.append({default: config_obj[config_obj.default_section][default]})
|
||||
{% if cookiecutter.rich_logging == "yes" -%}log.debug{%- else -%}print{%- endif %}(f"* {default} = {config_obj[config_obj.default_section][default]}")
|
||||
if default in internal_globals:
|
||||
{% if cookiecutter.rich_logging == "yes" -%}log.debug{%- else -%}print{%- endif %}(f": {default} = {config_obj[config_obj.default_section][default]}")
|
||||
else:
|
||||
{% if cookiecutter.rich_logging == "yes" -%}log.debug{%- else -%}print{%- endif %}(f"* {default} = {config_obj[config_obj.default_section][default]}")
|
||||
else:
|
||||
{% if cookiecutter.rich_logging == "yes" -%}log.debug{%- else -%}print{%- endif %}(f"No defaults defined")
|
||||
|
||||
@ -133,6 +142,11 @@ def is_default(
|
||||
return any(config_key in ini_default for ini_default in ini_defaults)
|
||||
|
||||
|
||||
def is_global(
|
||||
config_key: str) -> bool:
|
||||
return config_key in internal_globals
|
||||
|
||||
|
||||
def is_same_as_default(
|
||||
config_kv_pair: dict) -> bool:
|
||||
return config_kv_pair in ini_defaults
|
||||
@ -149,11 +163,17 @@ def validate_config_sections(
|
||||
else:
|
||||
for key in config_obj.options(this_section, no_defaults=True):
|
||||
kv_prefix = "~"
|
||||
if is_default(key):
|
||||
remove_from_section = False
|
||||
if is_global(key):
|
||||
kv_prefix = "#"
|
||||
remove_from_section = True
|
||||
elif is_default(key):
|
||||
kv_prefix = "+"
|
||||
if is_same_as_default({key: config_obj[this_section][key]}):
|
||||
kv_prefix = "="
|
||||
{% if cookiecutter.rich_logging == "yes" -%}log.debug{%- else -%}print{%- endif %}(f"{kv_prefix} {key} = {config_obj[this_section][key]}")
|
||||
if remove_from_section:
|
||||
config_obj.remove_option(this_section, key)
|
||||
{%- endif %}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user