|
27 | 27 | log = logging.getLogger(__name__) |
28 | 28 |
|
29 | 29 |
|
| 30 | +class _OrderedItem(t.TypedDict): |
| 31 | + """Ordered config entry preserving label/section pairs.""" |
| 32 | + |
| 33 | + label: str |
| 34 | + section: object |
| 35 | + |
| 36 | + |
30 | 37 | def create_add_subparser(parser: argparse.ArgumentParser) -> None: |
31 | 38 | """Create ``vcspull add`` argument subparser. |
32 | 39 |
|
@@ -164,40 +171,43 @@ def _normalize_detected_url(remote: str | None) -> tuple[str, str]: |
164 | 171 |
|
165 | 172 |
|
166 | 173 | def _build_ordered_items( |
167 | | - top_level_items: list[tuple[str, t.Any]] | None, |
168 | | - raw_config: dict[str, t.Any], |
169 | | -) -> list[dict[str, t.Any]]: |
| 174 | + top_level_items: list[tuple[str, object]] | None, |
| 175 | + raw_config: dict[str, object], |
| 176 | +) -> list[_OrderedItem]: |
170 | 177 | """Return deep-copied top-level items preserving original ordering.""" |
171 | | - source: list[tuple[str, t.Any]] = top_level_items or list(raw_config.items()) |
| 178 | + source: list[tuple[str, object]] = top_level_items or list(raw_config.items()) |
172 | 179 |
|
173 | | - ordered: list[dict[str, t.Any]] = [] |
| 180 | + ordered: list[_OrderedItem] = [] |
174 | 181 | for label, section in source: |
175 | 182 | ordered.append({"label": label, "section": copy.deepcopy(section)}) |
176 | 183 | return ordered |
177 | 184 |
|
178 | 185 |
|
179 | 186 | def _aggregate_from_ordered_items( |
180 | | - items: list[dict[str, t.Any]], |
181 | | -) -> dict[str, t.Any]: |
| 187 | + items: list[_OrderedItem], |
| 188 | +) -> dict[str, object]: |
182 | 189 | """Collapse ordered top-level items into a mapping grouped by label.""" |
183 | | - aggregated: dict[str, t.Any] = {} |
| 190 | + aggregated: dict[str, object] = {} |
184 | 191 | for entry in items: |
185 | 192 | label = entry["label"] |
186 | 193 | section = entry["section"] |
187 | 194 | if isinstance(section, dict): |
188 | | - workspace_section = aggregated.setdefault(label, {}) |
| 195 | + workspace_section = t.cast( |
| 196 | + "dict[str, object]", |
| 197 | + aggregated.setdefault(label, {}), |
| 198 | + ) |
189 | 199 | for repo_name, repo_config in section.items(): |
190 | | - workspace_section[repo_name] = copy.deepcopy(repo_config) |
| 200 | + workspace_section[str(repo_name)] = copy.deepcopy(repo_config) |
191 | 201 | else: |
192 | 202 | aggregated[label] = copy.deepcopy(section) |
193 | 203 | return aggregated |
194 | 204 |
|
195 | 205 |
|
196 | 206 | def _collect_duplicate_sections( |
197 | | - items: list[dict[str, t.Any]], |
198 | | -) -> dict[str, list[t.Any]]: |
| 207 | + items: list[_OrderedItem], |
| 208 | +) -> dict[str, list[object]]: |
199 | 209 | """Return mapping of labels to their repeated sections (>= 2 occurrences).""" |
200 | | - occurrences: dict[str, list[t.Any]] = {} |
| 210 | + occurrences: dict[str, list[object]] = {} |
201 | 211 | for entry in items: |
202 | 212 | label = entry["label"] |
203 | 213 | occurrences.setdefault(label, []).append(copy.deepcopy(entry["section"])) |
@@ -383,9 +393,9 @@ def add_repo( |
383 | 393 | config_file_path = home_configs[0] |
384 | 394 |
|
385 | 395 | # Load existing config |
386 | | - raw_config: dict[str, t.Any] |
387 | | - duplicate_root_occurrences: dict[str, list[t.Any]] |
388 | | - top_level_items: list[tuple[str, t.Any]] |
| 396 | + raw_config: dict[str, object] |
| 397 | + duplicate_root_occurrences: dict[str, list[object]] |
| 398 | + top_level_items: list[tuple[str, object]] |
389 | 399 | display_config_path = str(PrivatePath(config_file_path)) |
390 | 400 |
|
391 | 401 | if config_file_path.exists() and config_file_path.is_file(): |
@@ -439,7 +449,7 @@ def add_repo( |
439 | 449 | new_repo_entry = {"repo": url} |
440 | 450 |
|
441 | 451 | def _ensure_workspace_label_for_merge( |
442 | | - config_data: dict[str, t.Any], |
| 452 | + config_data: dict[str, object], |
443 | 453 | ) -> tuple[str, bool]: |
444 | 454 | workspace_map: dict[pathlib.Path, str] = {} |
445 | 455 | for label, section in config_data.items(): |
@@ -473,7 +483,7 @@ def _ensure_workspace_label_for_merge( |
473 | 483 | return workspace_label, relabelled |
474 | 484 |
|
475 | 485 | def _prepare_no_merge_items( |
476 | | - items: list[dict[str, t.Any]], |
| 486 | + items: list[_OrderedItem], |
477 | 487 | ) -> tuple[str, int, bool]: |
478 | 488 | matching_indexes: list[int] = [] |
479 | 489 | for idx, entry in enumerate(items): |
|
0 commit comments