lektor_ng.inifile

src/lektor_ng/inifile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import dataclasses as dc
import io
import os
from collections.abc import Generator, Iterator
from configparser import ConfigParser, MissingSectionHeaderError
from pathlib import Path
from typing import Any

GLOBAL_NAME = "xyz"


def config_parser_load(filename: str | Path) -> tuple[ConfigParser, bool]:
    path = Path(filename)
    config = ConfigParser()
    try:
        is_new = config.read(path)
        return config, not bool(is_new)
    except MissingSectionHeaderError:
        text = path.read_text()
        config.read_string(f"[{GLOBAL_NAME}]\n{text}")
        return config, False


@dc.dataclass
class IniFile:
    filename: str
    is_new: bool = False

    def __post_init__(self) -> None:
        self.filename = os.path.abspath(self.filename)
        self.config, self.is_new = config_parser_load(self.filename)

    def __iter__(self) -> Iterator[str]:
        if GLOBAL_NAME in self.config:
            for option in self.config[GLOBAL_NAME]:
                yield option

        for section in self.config:
            if section in {GLOBAL_NAME, "DEFAULT"}:
                continue
            for option in self.config[section]:
                yield f"{section}.{option}"

    def get(self, name: str, default: Any = None) -> Any:
        section, _, option = name.rpartition(".")
        while section.endswith("."):
            section, option = section[:-1], f".{option}"
        if not section:
            return self.config[GLOBAL_NAME].get(option, default)
        if section not in self.sections():
            return default
        return self.config[section].get(option, default)

    def items(self) -> Generator[tuple[str, Any], None, None]:
        for key in self:
            yield key, self.get(key)

    def __getitem__(self, name: str) -> Any:
        return self.get(name)

    def __setitem__(self, name: str, value: Any) -> None:
        section, _, option = name.rpartition(".")
        if not section:
            self.config[GLOBAL_NAME][option] = value
            return
        if section not in self.sections():
            self.config.add_section(section)
        self.config[section][option] = value

    def sections(self) -> list[str]:
        return self.config.sections()

    def section_as_dict(self, name: str) -> dict[str, Any]:
        result = {}
        for section in self.sections():
            if section != name:
                continue
            for option in self.config[section]:
                result[option] = self.config[section][option]
        return result

    def get_int(self, name: str, default: Any = None) -> int | None:
        value = self.get(name, default)
        if value is None:
            return None
        return int(value)

    def get_bool(self, name: str, default: Any = False) -> bool | None:
        value = self.get(name)
        if value is None:
            return None
        return bool(
            {
                "0": False,
                "no": False,
                "false": False,
                "1": True,
                "yes": True,
                "true": True,
            }.get(value, default)
        )

    def save(self, create_folder=False) -> None:
        raise NotImplementedError("not ready")
        buffer = io.StringIO()
        self.config.write(buffer)
        with open(self.filename, "w") as fp:
            fp.write(buffer.getvalue())