|
1 | 1 | import collections |
2 | 2 | import os |
3 | 3 | import yaml |
| 4 | +from pathlib import Path |
| 5 | +from typing import Mapping |
4 | 6 |
|
5 | 7 |
|
6 | 8 | class ConfigError(Exception): |
7 | 9 | pass |
8 | 10 |
|
9 | 11 |
|
10 | | -def load_config(configuration_file): |
| 12 | +def load_config(configuration_file: str, priority_dirs: list[Path] = []) -> dict: |
11 | 13 | """Load a problemtools configuration file. |
12 | 14 |
|
13 | 15 | Args: |
14 | 16 | configuration_file (str): name of configuration file. Name is |
15 | 17 | relative to config directory so typically just a file name |
16 | 18 | without paths, e.g. "languages.yaml". |
17 | 19 | """ |
18 | | - res = None |
| 20 | + res: dict | None = None |
19 | 21 |
|
20 | | - for dirname in __config_file_paths(): |
21 | | - path = os.path.join(dirname, configuration_file) |
| 22 | + for dirname in __config_file_paths() + priority_dirs: |
| 23 | + path = dirname / configuration_file |
22 | 24 | new_config = None |
23 | | - if os.path.isfile(path): |
| 25 | + if path.is_file(): |
24 | 26 | try: |
25 | 27 | with open(path, 'r') as config: |
26 | 28 | new_config = yaml.safe_load(config.read()) |
27 | 29 | except (yaml.parser.ParserError, yaml.scanner.ScannerError) as err: |
28 | | - raise ConfigError('Config file %s: failed to parse: %s' % (path, err)) |
| 30 | + raise ConfigError(f'Config file {path}: failed to parse: {err}') |
29 | 31 | if res is None: |
30 | 32 | if new_config is None: |
31 | | - raise ConfigError('Base configuration file %s not found in %s' % (configuration_file, path)) |
| 33 | + raise ConfigError(f'Base configuration file {configuration_file} not found in {path}') |
32 | 34 | res = new_config |
33 | 35 | elif new_config is not None: |
34 | 36 | __update_dict(res, new_config) |
35 | 37 |
|
| 38 | + assert res is not None, 'Failed to load config (should never happen, we should have hit an error in loop above)' |
36 | 39 | return res |
37 | 40 |
|
38 | 41 |
|
39 | | -def __config_file_paths(): |
| 42 | +def __config_file_paths() -> list[Path]: |
40 | 43 | """ |
41 | 44 | Paths in which to look for config files, by increasing order of |
42 | 45 | priority (i.e., any config in the last path should take precedence |
43 | 46 | over the others). |
44 | 47 | """ |
45 | 48 | return [ |
46 | | - os.path.join(os.path.dirname(__file__), 'config'), |
47 | | - os.path.join('/etc', 'kattis', 'problemtools'), |
48 | | - os.path.join(os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')), 'problemtools'), |
| 49 | + Path(__file__).parent / 'config', |
| 50 | + Path('/etc/kattis/problemtools'), |
| 51 | + Path(os.environ.get('XDG_CONFIG_HOME', Path.home() / '.config')) / 'problemtools', |
49 | 52 | ] |
50 | 53 |
|
51 | 54 |
|
52 | | -def __update_dict(orig, update): |
| 55 | +def __update_dict(orig: dict, update: Mapping) -> None: |
53 | 56 | """Deep update of a dictionary |
54 | 57 |
|
55 | 58 | For each entry (k, v) in update such that both orig[k] and v are |
|
0 commit comments