A desktop app keeps its settings in a small text file. Write read_settings(text) that returns the settings as a dictionary of strings. The rules:
- Lines may end with
\n,\r\nor\r. - A line ending in a backslash
\continues on the next line: the backslash is removed and the two lines are joined with exactly one space (spaces around the join are dropped). A continued line may continue again. - After joining, blank lines and lines whose first non-space character is
#or;are ignored. - A line
[name]starts a section. Settings before the first section have plain keys; inside a section the key issection.key. Section names and keys are lower-cased. - A setting is
key = value, split at the first=; the key and value are stripped. A later setting with the same key replaces the earlier one. - Any other line, or a setting with an empty key, is an error: raise
ValueErrorwith a message that starts withline N:whereNis the (1-based) number of the line where it starts.
The tests check errors with the helpers raises(fn, *args) and message(fn, *args), which return the name and the message of the exception raised (or None).
Examples
Input: text = "theme = dark\n[Editor]\nFont = Mono 12\n; old: size = 10\ntabs = 4\n"
Output: {"theme": "dark", "editor.font": "Mono 12", "editor.tabs": "4"}
Input: text = "path = /usr/bin \\\n /opt/bin\r\nurl = a=b"
Output: {"path": "/usr/bin /opt/bin", "url": "a=b"}
Input: raises(read_settings, "ok = 1\njust some words\n")
Output: "ValueError" (the message starts with "line 2:")
Constraints
len(text) <= 10**6.- A backslash on the very last line is simply removed.
Goals
- Split text into lines with `splitlines`, whatever the line endings
- Join a line with the next one by pulling it from the iterator with `next`
- Raise `ValueError` with the line number for a line that cannot be understood