| 229 | |
| 230 | |
| 231 | class PluginLoader(SourceFileLoader): |
| 232 | def __init__(self, manager: PluginManager, fullname: str, path: str) -> None: |
| 233 | self.manager = manager |
| 234 | self.loaded = False |
| 235 | super().__init__(fullname, path) |
| 236 | |
| 237 | def create_module(self, spec) -> ModuleType | None: |
| 238 | if self.name in sys.modules: |
| 239 | self.loaded = True |
| 240 | return sys.modules[self.name] |
| 241 | # return None to use default module creation |
| 242 | return super().create_module(spec) |
| 243 | |
| 244 | def exec_module(self, module: ModuleType) -> None: |
| 245 | if self.loaded: |
| 246 | return |
| 247 | |
| 248 | # create plugin before executing |
| 249 | plugin = _new_plugin(self.name, module, self.manager) |
| 250 | setattr(module, "__plugin__", plugin) |
| 251 | |
| 252 | # enter plugin context |
| 253 | _plugin_token = _current_plugin.set(plugin) |
| 254 | |
| 255 | try: |
| 256 | super().exec_module(module) |
| 257 | except Exception: |
| 258 | _revert_plugin(plugin) |
| 259 | raise |
| 260 | finally: |
| 261 | # leave plugin context |
| 262 | _current_plugin.reset(_plugin_token) |
| 263 | |
| 264 | # get plugin metadata |
| 265 | metadata: PluginMetadata | None = getattr(module, "__plugin_meta__", None) |
| 266 | plugin.metadata = metadata |
| 267 | |
| 268 | return |
| 269 | |
| 270 | |
| 271 | sys.meta_path.insert(0, PluginFinder()) |