Skip to content

jmcore.config_file

jmcore.config_file

Narrow TOML config access for the shell TUI.

The TUI only needs to read, set, and remove string values in named top-level tables. Keeping that surface here avoids shell parsing and preserves comments and unrelated configuration through TOMLKit.

Classes

ConfigFileError

Bases: Exception

Raised when a config file cannot be safely handled by the TUI.

Source code in jmcore/src/jmcore/config_file.py
23
24
class ConfigFileError(Exception):
    """Raised when a config file cannot be safely handled by the TUI."""

Functions:

get_config_value(path: Path, section: str, key: str) -> str | None

Return a string config value, or None when the file or key is absent.

Source code in jmcore/src/jmcore/config_file.py
56
57
58
59
60
61
62
63
64
65
66
67
def get_config_value(path: Path, section: str, key: str) -> str | None:
    """Return a string config value, or ``None`` when the file or key is absent."""
    _validate_identifier("section", section)
    _validate_identifier("key", key)
    table = _get_table(_read_document(path), section)
    if table is None or key not in table:
        return None

    value = table[key]
    if not isinstance(value, str):
        raise ConfigFileError("config value is not a string")
    return value

main(argv: Sequence[str] | None = None) -> int

Run the TUI config helper command.

Source code in jmcore/src/jmcore/config_file.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def main(argv: Sequence[str] | None = None) -> int:
    """Run the TUI config helper command."""
    args = _parse_args(argv)
    try:
        if args.command == "get":
            value = get_config_value(args.config, args.section, args.key)
            if value is not None:
                sys.stdout.write(value)
        elif args.command == "set":
            try:
                value = sys.stdin.buffer.read().decode("utf-8")
            except UnicodeDecodeError as exc:
                raise ConfigFileError("config value must be UTF-8 text") from exc
            set_config_value(args.config, args.section, args.key, value)
        else:
            remove_config_value(args.config, args.section, args.key)
    except ConfigFileError as exc:
        print(f"config file error: {exc}", file=sys.stderr)
        return 2
    return 0

remove_config_value(path: Path, section: str, key: str) -> None

Remove a string config value, doing nothing when the file or key is absent.

Source code in jmcore/src/jmcore/config_file.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def remove_config_value(path: Path, section: str, key: str) -> None:
    """Remove a string config value, doing nothing when the file or key is absent."""
    _validate_identifier("section", section)
    _validate_identifier("key", key)
    if not path.exists():
        return

    document = _read_document(path)
    table = _get_table(document, section)
    if table is None or key not in table:
        return

    value = table[key]
    if not isinstance(value, str):
        raise ConfigFileError("config value is not a string")
    del table[key]
    try:
        atomic_write_sensitive_file(path, tomlkit.dumps(document).encode("utf-8"))
    except OSError as exc:
        raise ConfigFileError("cannot write config") from exc

set_config_value(path: Path, section: str, key: str, value: str) -> None

Set a string config value without rewriting the file for a no-op.

Source code in jmcore/src/jmcore/config_file.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def set_config_value(path: Path, section: str, key: str, value: str) -> None:
    """Set a string config value without rewriting the file for a no-op."""
    _validate_identifier("section", section)
    _validate_identifier("key", key)
    document = _read_document(path)
    table = _get_table(document, section)
    if table is None:
        table = tomlkit.table()
        document[section] = table
    elif key in table:
        current_value = table[key]
        if not isinstance(current_value, str):
            raise ConfigFileError("config value is not a string")
        if current_value == value:
            return

    table[key] = value
    try:
        atomic_write_sensitive_file(path, tomlkit.dumps(document).encode("utf-8"))
    except OSError as exc:
        raise ConfigFileError("cannot write config") from exc