|
1 | 1 | """Module to hold various utils.""" |
2 | 2 |
|
3 | | -from collections.abc import Generator |
4 | | -from pathlib import Path |
5 | | -from typing import Any |
6 | | -from typing import Optional |
| 3 | +from maison import types |
7 | 4 |
|
8 | | -from maison.config_sources.base_source import BaseSource |
9 | | -from maison.config_sources.ini_source import IniSource |
10 | | -from maison.config_sources.pyproject_source import PyprojectSource |
11 | | -from maison.config_sources.toml_source import TomlSource |
12 | 5 |
|
13 | | - |
14 | | -def path_contains_file(path: Path, filename: str) -> bool: |
15 | | - """Determine whether a file exists in the given path. |
16 | | -
|
17 | | - Args: |
18 | | - path: the path in which to search for the file |
19 | | - filename: the name of the file |
20 | | -
|
21 | | - Returns: |
22 | | - A boolean to indicate whether the given file exists in the given path |
23 | | - """ |
24 | | - return (path / filename).is_file() |
25 | | - |
26 | | - |
27 | | -def get_file_path( |
28 | | - filename: str, starting_path: Optional[Path] = None |
29 | | -) -> Optional[Path]: |
30 | | - """Search for a file by traversing up the tree from a path. |
31 | | -
|
32 | | - Args: |
33 | | - filename: the name of the file or an absolute path to a config to search for |
34 | | - starting_path: an optional path from which to start searching |
35 | | -
|
36 | | - Returns: |
37 | | - The `Path` to the file if it exists or `None` if it doesn't |
38 | | - """ |
39 | | - filename_path = Path(filename).expanduser() |
40 | | - if filename_path.is_absolute() and filename_path.is_file(): |
41 | | - return filename_path |
42 | | - |
43 | | - start = starting_path or Path.cwd() |
44 | | - |
45 | | - for path in _generate_search_paths(starting_path=start): |
46 | | - if path_contains_file(path=path, filename=filename): |
47 | | - return path / filename |
48 | | - |
49 | | - return None |
50 | | - |
51 | | - |
52 | | -def _generate_search_paths(starting_path: Path) -> Generator[Path, None, None]: |
53 | | - """Generate paths from a starting path and traversing up the tree. |
54 | | -
|
55 | | - Args: |
56 | | - starting_path: a starting path to start yielding search paths |
57 | | -
|
58 | | - Yields: |
59 | | - a path from the tree |
60 | | - """ |
61 | | - yield from [starting_path, *starting_path.parents] |
62 | | - |
63 | | - |
64 | | -def _collect_configs( |
65 | | - package_name: str, |
66 | | - source_files: list[str], |
67 | | - starting_path: Optional[Path] = None, |
68 | | -) -> list[BaseSource]: |
69 | | - """Collect configs and return them in a list. |
70 | | -
|
71 | | - Args: |
72 | | - package_name: the name of the package to be used to find the right section in |
73 | | - the config file |
74 | | - source_files: a list of source config filenames to look for. |
75 | | - starting_path: an optional starting path to start the search |
76 | | -
|
77 | | - Returns: |
78 | | - a list of the found config sources |
79 | | - """ |
80 | | - sources: list[BaseSource] = [] |
81 | | - |
82 | | - for source in source_files: |
83 | | - file_path = get_file_path( |
84 | | - filename=source, |
85 | | - starting_path=starting_path, |
86 | | - ) |
87 | | - |
88 | | - if not file_path: |
89 | | - continue |
90 | | - |
91 | | - # dict[str, Any] to stop mypy complaining: |
92 | | - # https://github.com/python/mypy/issues/5382#issuecomment-583901369 |
93 | | - source_kwargs: dict[str, Any] = { |
94 | | - "filepath": file_path, |
95 | | - "package_name": package_name, |
96 | | - } |
97 | | - |
98 | | - if source.endswith("toml"): |
99 | | - if source.startswith("pyproject"): |
100 | | - sources.append(PyprojectSource(**source_kwargs)) |
101 | | - else: |
102 | | - sources.append(TomlSource(**source_kwargs)) |
103 | | - |
104 | | - if source.endswith("ini"): |
105 | | - sources.append(IniSource(**source_kwargs)) |
106 | | - |
107 | | - return sources |
108 | | - |
109 | | - |
110 | | -def deep_merge(destination: dict[Any, Any], source: dict[Any, Any]) -> dict[Any, Any]: |
| 6 | +def deep_merge( |
| 7 | + destination: types.ConfigValues, source: types.ConfigValues |
| 8 | +) -> types.ConfigValues: |
111 | 9 | """Recursively updates the destination dictionary. |
112 | 10 |
|
113 | 11 | Usage example: |
@@ -141,7 +39,7 @@ def deep_merge(destination: dict[Any, Any], source: dict[Any, Any]) -> dict[Any, |
141 | 39 | raise RuntimeError( |
142 | 40 | f"Cannot merge dict '{src_value}' into type '{type(dest_node)}'" |
143 | 41 | ) |
144 | | - deep_merge(dest_node, src_value) |
| 42 | + _ = deep_merge(dest_node, src_value) |
145 | 43 | else: |
146 | 44 | destination[key] = src_value |
147 | 45 |
|
|
0 commit comments