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
|
"""
Implements loading and providing the configuration of the radar instance. Usage should be straight-forward:
>>> from . import _config
>>> config = _config.load()
>>> config.locations
['New York City', 'Jersey City']
An example configuration file looks like this:
[Waterspout Radar]
db = .waterspout/db.json
key = <secret key>
[Locations]
New York City
Jersey City
"""
import configparser
import dataclasses
import os
import typing
@dataclasses.dataclass
class Config:
db: str
key: str
locations: typing.List[str]
class ConfigError(Exception):
"Raised when configuration could not be loaded or was malformed."
def effective_pathname(pathname: str = None) -> str:
"Pathname of the configuration file based on optional *pathname*, environment, and default."
return pathname or os.getenv("WATERSPOUT_RADAR_CONFIG") or "waterspout-radar.ini"
def load(pathname: str) -> Config:
"Loads configuration from a file at *pathname*. May raise ConfigError when content does not meet expectations."
config = configparser.ConfigParser(allow_no_value=True)
config.read(pathname)
raw = {}
errors = []
def _load_key(section, name, target=None, type=None, default=None, required=False):
target = target or name
try:
value = config[section][name]
if type:
value = type(value)
raw[target] = value
except (KeyError, ValueError):
if default:
raw[target] = default
if required:
errors.append(f"Config is missing required field: {pathname}: {section}: {name}")
_load_key("Waterspout Radar", "db", default="radar.json")
_load_key("Waterspout Radar", "key", required=True)
try:
raw["locations"] = list(config["Locations"])
except KeyError:
errors.append(f"Config is missing Locations: {pathname}")
if errors:
raise ConfigError(*errors)
return Config(**raw)
|