|
| 1 | +def get_application_module_refs(application: object | str, specification: dict) -> list: |
| 2 | + """ |
| 3 | + Given an application object (or application ref string) and the full specification, |
| 4 | + return a deduplicated, alphabetical list of module reference strings. |
| 5 | +
|
| 6 | + - Accepts either the application dict or the application reference (string). |
| 7 | + - Handles modules listed as either strings or dicts with a "module" key. |
| 8 | + - If the application has an 'extends' attribute (string or list), modules from |
| 9 | + parent application(s) are included. |
| 10 | + - Prevents infinite loops in extends chains by tracking visited application refs. |
| 11 | + """ |
| 12 | + applications = specification.get("application", {}) or {} |
| 13 | + collected = set() |
| 14 | + visited_apps = set() |
| 15 | + |
| 16 | + # normalize input: application may be a ref (str) or the application dict itself |
| 17 | + if isinstance(application, str): |
| 18 | + app_obj = applications.get(application) |
| 19 | + if not app_obj: |
| 20 | + return [] |
| 21 | + elif hasattr(application, "get") and callable(getattr(application, "get")): |
| 22 | + # Accept frontmatter.Post which expose a `.get()` method. and dicts |
| 23 | + app_obj = application |
| 24 | + else: |
| 25 | + return [] |
| 26 | + |
| 27 | + def extract_module_ref(entry): |
| 28 | + """Return a module ref string from an entry, or None. |
| 29 | +
|
| 30 | + Entry may be: |
| 31 | + - a string 'module-ref' |
| 32 | + - a dict {'module': 'module-ref'} |
| 33 | + - nested dicts where the value of 'module' is a dict (rare) |
| 34 | + """ |
| 35 | + if isinstance(entry, str): |
| 36 | + return entry |
| 37 | + if isinstance(entry, dict): |
| 38 | + # prefer explicit 'module' key |
| 39 | + if "module" in entry: |
| 40 | + val = entry["module"] |
| 41 | + # recurse if nested |
| 42 | + return extract_module_ref(val) |
| 43 | + # fallback: if dict has a single string value |
| 44 | + for v in entry.values(): |
| 45 | + ref = extract_module_ref(v) |
| 46 | + if ref: |
| 47 | + return ref |
| 48 | + return None |
| 49 | + |
| 50 | + def collect_from_app(app_obj): |
| 51 | + # collect modules from this app object; support 'modules' or singular 'module' |
| 52 | + mods = app_obj.get("modules", None) |
| 53 | + if mods is None: |
| 54 | + mods = app_obj.get("module", []) |
| 55 | + # if mods is a dict, iterate its values |
| 56 | + if isinstance(mods, dict): |
| 57 | + mods_iter = list(mods.values()) |
| 58 | + else: |
| 59 | + mods_iter = mods or [] |
| 60 | + |
| 61 | + for m in mods_iter: |
| 62 | + mod_ref = extract_module_ref(m) |
| 63 | + if isinstance(mod_ref, str) and mod_ref: |
| 64 | + collected.add(mod_ref) |
| 65 | + |
| 66 | + # follow extends if present |
| 67 | + extends = app_obj.get("extends") |
| 68 | + if not extends: |
| 69 | + return |
| 70 | + |
| 71 | + parent_refs = extends if isinstance(extends, list) else [extends] |
| 72 | + for pref in parent_refs: |
| 73 | + if not isinstance(pref, str) or not pref: |
| 74 | + continue |
| 75 | + if pref in visited_apps: |
| 76 | + continue |
| 77 | + visited_apps.add(pref) |
| 78 | + parent_app = applications.get(pref) |
| 79 | + if parent_app: |
| 80 | + collect_from_app(parent_app) |
| 81 | + |
| 82 | + # start collection from the resolved app_obj |
| 83 | + collect_from_app(app_obj) |
| 84 | + return sorted(collected) |
| 85 | + |
| 86 | + |
| 87 | +if __name__ == "__main__": |
| 88 | + print("Testing information model generation script.") |
| 89 | + |
| 90 | + try: |
| 91 | + from loader import load_content |
| 92 | + |
| 93 | + specification = load_content() |
| 94 | + print("Specification loaded successfully") |
| 95 | + |
| 96 | + result = get_application_module_refs( |
| 97 | + specification["application"]["ldc-prospective-use"], specification |
| 98 | + ) |
| 99 | + print(result) |
| 100 | + |
| 101 | + except Exception as e: |
| 102 | + print(f"✗ Error: {e}") |
0 commit comments