feat(python-naive): Add concept of non-overridable globals

This commit is contained in:
hygienic-books 2022-06-20 03:50:52 +02:00
parent 7e971bc330
commit e265dc3853
2 changed files with 63 additions and 23 deletions

View File

@ -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
# ...

View File

@ -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 %}