|
28 | 28 |
|
29 | 29 | ''' |
30 | 30 |
|
| 31 | +# PackageManager.py (MicroPython) |
31 | 32 |
|
32 | | -class PackageManager(): |
| 33 | +import os |
| 34 | + |
| 35 | +class PackageManager: |
| 36 | + """Registry of all discovered apps. |
| 37 | +
|
| 38 | + * PackageManager.get_app_list() -> list of App objects (sorted by name) |
| 39 | + * PackageManager[fullname] -> App (raises KeyError if missing) |
| 40 | + * PackageManager.get(fullname) -> App or None |
| 41 | + """ |
33 | 42 |
|
34 | | - app_list = [] # list of App objects, sorted alphabetically by app.name, unique by full_name (com.example.appname) |
| 43 | + # --------------------------------------------------------------------- # |
| 44 | + # internal storage |
| 45 | + # --------------------------------------------------------------------- # |
| 46 | + _app_list = [] # sorted by app.name |
| 47 | + _by_fullname = {} # fullname -> App |
35 | 48 |
|
| 49 | + # --------------------------------------------------------------------- # |
| 50 | + # public list access (kept for backward compatibility) |
| 51 | + # --------------------------------------------------------------------- # |
36 | 52 | @classmethod |
37 | 53 | def get_app_list(cls): |
38 | | - if len(cls.app_list) == 0: |
| 54 | + if not cls._app_list: |
39 | 55 | cls.find_apps() |
40 | | - return cls.app_list |
| 56 | + return cls._app_list |
| 57 | + |
| 58 | + # --------------------------------------------------------------------- # |
| 59 | + # dict-style lookup: PackageManager["com.example.myapp"] |
| 60 | + # --------------------------------------------------------------------- # |
| 61 | + def __class_getitem__(cls, fullname): |
| 62 | + try: |
| 63 | + return cls._by_fullname[fullname] |
| 64 | + except KeyError: |
| 65 | + raise KeyError("No app with fullname='{}'".format(fullname)) |
41 | 66 |
|
| 67 | + # --------------------------------------------------------------------- # |
| 68 | + # safe lookup: PackageManager.get("com.example.myapp") -> App or None |
| 69 | + # --------------------------------------------------------------------- # |
| 70 | + @classmethod |
| 71 | + def get(cls, fullname): |
| 72 | + return cls._by_fullname.get(fullname) |
| 73 | + |
| 74 | + # --------------------------------------------------------------------- # |
| 75 | + # discovery & population |
| 76 | + # --------------------------------------------------------------------- # |
42 | 77 | @classmethod |
43 | 78 | def find_apps(cls): |
44 | 79 | print("\n\n\nPackageManager finding apps...") |
45 | | - seen_fullnames = set() |
46 | | - # Check and collect subdirectories from existing directories |
47 | | - apps_dir = "apps" |
| 80 | + |
| 81 | + seen = set() # avoid processing the same fullname twice |
| 82 | + apps_dir = "apps" |
48 | 83 | apps_dir_builtin = "builtin/apps" |
49 | 84 |
|
50 | | - # Check and collect unique subdirectories |
51 | | - for dir_path in [apps_dir, apps_dir_builtin]: |
| 85 | + for base in (apps_dir, apps_dir_builtin): |
52 | 86 | try: |
53 | | - if os.stat(dir_path)[0] & 0x4000: # Verify directory exists |
| 87 | + # ---- does the directory exist? -------------------------------- |
| 88 | + st = os.stat(base) |
| 89 | + if not (st[0] & 0x4000): # 0x4000 = directory bit |
| 90 | + continue |
| 91 | + |
| 92 | + # ---- iterate over immediate children ------------------------- |
| 93 | + for name in os.listdir(base): |
| 94 | + full_path = "{}/{}".format(base, name) |
| 95 | + |
| 96 | + # ---- is it a directory? --------------------------------- |
54 | 97 | try: |
55 | | - for d in os.listdir(dir_path): |
56 | | - full_path = f"{dir_path}/{d}" |
57 | | - print(f"full_path: {full_path}") |
58 | | - try: |
59 | | - if os.stat(full_path)[0] & 0x4000: # Check if it's a directory |
60 | | - fullname = d |
61 | | - if fullname not in seen_fullnames: # Avoid duplicates |
62 | | - seen_fullnames.add(fullname) |
63 | | - app = mpos.apps.parse_manifest(full_path) |
64 | | - cls.app_list.append(app) |
65 | | - print(f"added app {app}") |
66 | | - except Exception as e: |
67 | | - print(f"PackageManager: stat of {full_path} got exception: {e}") |
| 98 | + st = os.stat(full_path) |
| 99 | + if not (st[0] & 0x4000): |
| 100 | + continue |
68 | 101 | except Exception as e: |
69 | | - print(f"PackageManager: listdir of {dir_path} got exception: {e}") |
| 102 | + print("PackageManager: stat of {} got exception: {}".format(full_path, e)) |
| 103 | + continue |
| 104 | + |
| 105 | + fullname = name |
| 106 | + |
| 107 | + # ---- skip duplicates ------------------------------------ |
| 108 | + if fullname in seen: |
| 109 | + continue |
| 110 | + seen.add(fullname) |
| 111 | + |
| 112 | + # ---- parse the manifest --------------------------------- |
| 113 | + try: |
| 114 | + app = mpos.apps.parse_manifest(full_path) |
| 115 | + except Exception as e: |
| 116 | + print("PackageManager: parsing {} failed: {}".format(full_path, e)) |
| 117 | + continue |
| 118 | + |
| 119 | + # ---- store in both containers --------------------------- |
| 120 | + cls._app_list.append(app) |
| 121 | + cls._by_fullname[fullname] = app |
| 122 | + print("added app {}".format(app)) |
| 123 | + |
70 | 124 | except Exception as e: |
71 | | - print(f"PackageManager: stat of {dir_path} got exception: {e}") |
| 125 | + print("PackageManager: handling {} got exception: {}".format(base, e)) |
| 126 | + |
| 127 | + # ---- sort the list by display name (case-insensitive) ------------ |
| 128 | + cls._app_list.sort(key=lambda a: a.name.lower()) |
72 | 129 |
|
73 | | - # Sort apps alphabetically by app.name |
74 | | - cls.app_list.sort(key=lambda x: x.name.lower()) # Case-insensitive sorting by name |
75 | 130 |
|
76 | 131 | @staticmethod |
77 | 132 | def uninstall_app(app_fullname): |
|
0 commit comments