mirror of
https://github.com/devine-dl/devine.git
synced 2025-05-04 19:49:44 +00:00
I've moved log.info calls to console.log calls to reduce conflicts of logs at the same time as console refreshes, but also to reduce unnecessary log level text being printed to the console. We don't need to know if a log is an `info` level log, but I've kept log.error's and such as we would want to know if a log is an error log and such.
92 lines
3.2 KiB
Python
92 lines
3.2 KiB
Python
import ast
|
|
import logging
|
|
import sys
|
|
|
|
import click
|
|
from ruamel.yaml import YAML
|
|
|
|
from devine.core.config import config
|
|
from devine.core.console import console
|
|
from devine.core.constants import context_settings
|
|
|
|
|
|
@click.command(
|
|
short_help="Manage configuration values for the program and its services.",
|
|
context_settings=context_settings)
|
|
@click.argument("key", type=str, required=False)
|
|
@click.argument("value", type=str, required=False)
|
|
@click.option("--unset", is_flag=True, default=False, help="Unset/remove the configuration value.")
|
|
@click.option("--list", "list_", is_flag=True, default=False, help="List all set configuration values.")
|
|
@click.pass_context
|
|
def cfg(ctx: click.Context, key: str, value: str, unset: bool, list_: bool) -> None:
|
|
"""
|
|
Manage configuration values for the program and its services.
|
|
|
|
\b
|
|
Known Issues:
|
|
- Config changes remove all comments of the changed files, which may hold critical data. (#14)
|
|
"""
|
|
if not key and not value and not list_:
|
|
raise click.UsageError("Nothing to do.", ctx)
|
|
|
|
if value:
|
|
try:
|
|
value = ast.literal_eval(value)
|
|
except (ValueError, SyntaxError):
|
|
pass # probably a str without quotes or similar, assume it's a string value
|
|
|
|
log = logging.getLogger("cfg")
|
|
|
|
config_path = config.directories.user_configs / config.filenames.root_config
|
|
|
|
yaml, data = YAML(), None
|
|
yaml.default_flow_style = False
|
|
if config_path.is_file():
|
|
data = yaml.load(config_path)
|
|
|
|
if not data:
|
|
log.warning(f"{config_path} has no configuration data, yet")
|
|
# yaml.load() returns `None` if the input data is blank instead of a usable object
|
|
# force a usable object by making one and removing the only item within it
|
|
data = yaml.load("""__TEMP__: null""")
|
|
del data["__TEMP__"]
|
|
|
|
if list_:
|
|
yaml.dump(data, sys.stdout)
|
|
return
|
|
|
|
key_items = key.split(".")
|
|
parent_key = key_items[:-1]
|
|
trailing_key = key_items[-1]
|
|
|
|
is_write = value is not None
|
|
is_delete = unset
|
|
if is_write and is_delete:
|
|
raise click.ClickException("You cannot set a value and use --unset at the same time.")
|
|
|
|
if not is_write and not is_delete:
|
|
data = data.mlget(key_items, default=KeyError)
|
|
if data == KeyError:
|
|
raise click.ClickException(f"Key '{key}' does not exist in the config.")
|
|
yaml.dump(data, sys.stdout)
|
|
else:
|
|
try:
|
|
parent_data = data
|
|
if parent_key:
|
|
parent_data = data.mlget(parent_key, default=data)
|
|
if parent_data == data:
|
|
for key in parent_key:
|
|
if not hasattr(parent_data, key):
|
|
parent_data[key] = {}
|
|
parent_data = parent_data[key]
|
|
if is_write:
|
|
parent_data[trailing_key] = value
|
|
console.log(f"Set {key} to {repr(value)}")
|
|
elif is_delete:
|
|
del parent_data[trailing_key]
|
|
console.log(f"Unset {key}")
|
|
except KeyError:
|
|
raise click.ClickException(f"Key '{key}' does not exist in the config.")
|
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
yaml.dump(data, config_path)
|