ap_scanner/config.py (2623B)
1 """Configuration file support using configparser.
2
3 Default path: ~/.config/ap-scanner/config.ini
4 CLI arguments always take precedence over config file values.
5 """
6
7 import configparser
8 from pathlib import Path
9
10 DEFAULT_CONFIG_PATH = Path.home() / ".config" / "ap-scanner" / "config.ini"
11
12 # Defaults that match CLI defaults
13 DEFAULTS = {
14 "interface": "",
15 "channels": "",
16 "band": "all",
17 "sort": "rssi",
18 "no_color": "false",
19 "verbose": "false",
20 "log_file": "",
21 "write": "",
22 "dwell": "",
23 }
24
25
26 def load_config(config_path: str | None = None) -> dict[str, str]:
27 """Load configuration from an INI file.
28
29 Returns a dict of setting name -> string value.
30 Missing keys are filled with defaults.
31 """
32 path = Path(config_path) if config_path else DEFAULT_CONFIG_PATH
33 result = dict(DEFAULTS)
34
35 if not path.is_file():
36 return result
37
38 parser = configparser.ConfigParser()
39 parser.read(path, encoding="utf-8")
40
41 if parser.has_section("scanner"):
42 for key in DEFAULTS:
43 if parser.has_option("scanner", key):
44 result[key] = parser.get("scanner", key)
45
46 return result
47
48
49 def apply_config_to_args(args, config: dict[str, str]) -> None:
50 """Apply config file values to argparse namespace where CLI didn't override.
51
52 CLI arguments (non-default values) always take precedence.
53 """
54 # interface: CLI default is None
55 if args.interface is None and config["interface"]:
56 args.interface = config["interface"]
57
58 # channels: CLI default is None
59 if args.channels is None and config["channels"]:
60 args.channels = config["channels"]
61
62 # band: CLI default is "all"
63 if args.band == "all" and config["band"] != "all":
64 args.band = config["band"]
65
66 # sort: CLI default is "rssi"
67 if args.sort == "rssi" and config["sort"] != "rssi":
68 args.sort = config["sort"]
69
70 # no_color: CLI default is False
71 if not args.no_color and config["no_color"].lower() == "true":
72 args.no_color = True
73
74 # verbose: CLI default is False
75 if not args.verbose and config["verbose"].lower() == "true":
76 args.verbose = True
77
78 # log_file: CLI default is None
79 if args.log_file is None and config["log_file"]:
80 args.log_file = config["log_file"]
81
82 # write: CLI default is None
83 if args.write is None and config["write"]:
84 args.write = config["write"]
85
86 # dwell: CLI default is None
87 if getattr(args, "dwell", None) is None and config.get("dwell"):
88 try:
89 args.dwell = float(config["dwell"])
90 except ValueError:
91 pass