(self)
| 548 | ) |
| 549 | |
| 550 | def build_per_module_cache(self) -> None: |
| 551 | self._per_module_cache = {} |
| 552 | |
| 553 | # Config precedence is as follows: |
| 554 | # 1. Concrete section names: foo.bar.baz |
| 555 | # 2. "Unstructured" glob patterns: foo.*.baz, in the order |
| 556 | # they appear in the file (last wins) |
| 557 | # 3. "Well-structured" wildcard patterns: foo.bar.*, in specificity order. |
| 558 | |
| 559 | # Since structured configs inherit from structured configs above them in the hierarchy, |
| 560 | # we need to process per-module configs in a careful order. |
| 561 | # We have to process foo.* before foo.bar.* before foo.bar, |
| 562 | # and we need to apply *.bar to foo.bar but not to foo.bar.*. |
| 563 | # To do this, process all well-structured glob configs before non-glob configs and |
| 564 | # exploit the fact that foo.* sorts earlier ASCIIbetically (unicodebetically?) |
| 565 | # than foo.bar.*. |
| 566 | # (A section being "processed last" results in its config "winning".) |
| 567 | # Unstructured glob configs are stored and are all checked for each module. |
| 568 | unstructured_glob_keys = [k for k in self.per_module_options.keys() if "*" in k[:-1]] |
| 569 | structured_keys = [k for k in self.per_module_options.keys() if "*" not in k[:-1]] |
| 570 | wildcards = sorted(k for k in structured_keys if k.endswith(".*")) |
| 571 | concrete = [k for k in structured_keys if not k.endswith(".*")] |
| 572 | |
| 573 | for glob in unstructured_glob_keys: |
| 574 | self._glob_options.append((glob, self.compile_glob(glob))) |
| 575 | |
| 576 | # We (for ease of implementation) treat unstructured glob |
| 577 | # sections as used if any real modules use them or if any |
| 578 | # concrete config sections use them. This means we need to |
| 579 | # track which get used while constructing. |
| 580 | self._unused_configs = set(unstructured_glob_keys) |
| 581 | |
| 582 | for key in wildcards + concrete: |
| 583 | # Find what the options for this key would be, just based |
| 584 | # on inheriting from parent configs. |
| 585 | options = self.clone_for_module(key) |
| 586 | # And then update it with its per-module options. |
| 587 | self._per_module_cache[key] = options.apply_changes(self.per_module_options[key]) |
| 588 | |
| 589 | # Add the more structured sections into unused configs, since |
| 590 | # they only count as used if actually used by a real module. |
| 591 | self._unused_configs.update(structured_keys) |
| 592 | |
| 593 | def clone_for_module(self, module: str) -> Options: |
| 594 | """Create an Options object that incorporates per-module options. |
no test coverage detected