From 6bdd31e8ff8bf32a63924c6f196b818717aa0c9a Mon Sep 17 00:00:00 2001 From: Abigail Austin <38941820+ArachnidAbby@users.noreply.github.com> Date: Sun, 6 Sep 2026 13:56:04 -0400 Subject: [PATCH 1/3] Fix plugin version checking (backwards), fix module searching, ensure isolation in sub-modules, fix more stuff regarding loading plugin folder --- example/exampleplugin/src/plugin.toml | 2 +- example/plugins/folderplugin/plugin.toml | 2 +- main.py | 4 +- openpluginloader/__main__.py | 4 +- openpluginloader/defaultstrategy.py | 56 ++++++++++++++++++++++-- openpluginloader/manager.py | 8 ++-- other_file.py | 1 + 7 files changed, 63 insertions(+), 14 deletions(-) create mode 100644 other_file.py diff --git a/example/exampleplugin/src/plugin.toml b/example/exampleplugin/src/plugin.toml index 6ba0ddd..c1cbbe7 100644 --- a/example/exampleplugin/src/plugin.toml +++ b/example/exampleplugin/src/plugin.toml @@ -4,4 +4,4 @@ entry = "main" version = "1.0" min_api_version = "0.1.0" -max_api_version = "0.1.10" +max_api_version = "1.1.10" diff --git a/example/plugins/folderplugin/plugin.toml b/example/plugins/folderplugin/plugin.toml index 026f853..2d512d0 100644 --- a/example/plugins/folderplugin/plugin.toml +++ b/example/plugins/folderplugin/plugin.toml @@ -4,4 +4,4 @@ entry = "main" version = "1.0" min_api_version = "0.1.0" -max_api_version = "0.1.10" +max_api_version = "1.1.10" diff --git a/main.py b/main.py index 6c62bae..81532be 100644 --- a/main.py +++ b/main.py @@ -74,7 +74,7 @@ if __name__ == "__main__": - manager = create_default_manager(API_VERSION, Path("example/exampleplugin/build/")) + manager = create_default_manager(API_VERSION, Path("example/plugins/")) manager.initialize_hooks() plugin_src = Path("example/exampleplugin") @@ -105,4 +105,4 @@ print(sort_plugins(plugins)) print() - # manager.load_all_plugins() + manager.load_all_plugins() diff --git a/openpluginloader/__main__.py b/openpluginloader/__main__.py index 53ff52a..55c5fb9 100644 --- a/openpluginloader/__main__.py +++ b/openpluginloader/__main__.py @@ -14,8 +14,8 @@ def archive_plugin(): """Builds a plugin into a targz""" - src = CWD if len(sys.argv) < 1 else Path(sys.argv[0]) - dest = DEFAULT_DEST if len(sys.argv) < 2 else Path(sys.argv[1]) + src = CWD if len(sys.argv) < 1 else CWD / sys.argv[0] + dest = DEFAULT_DEST if len(sys.argv) < 2 else CWD / sys.argv[1] meta_loader = DefaultMetadataLoader() archiver = DefaultPluginArchiver() diff --git a/openpluginloader/defaultstrategy.py b/openpluginloader/defaultstrategy.py index 7a608f9..2a13531 100644 --- a/openpluginloader/defaultstrategy.py +++ b/openpluginloader/defaultstrategy.py @@ -316,17 +316,65 @@ def __init__(self, tar_file_path: Path, sub_path: Path): self.tar_file_path = tar_file_path self.sub_path = sub_path - def find_spec(self, fullname: str, path, target=None) -> ModuleSpec | None: - fullname_path = self.sub_path / "/".join(fullname.split(".")) + def _find_spec_folder( + self, fullname: str, path: str | None, target=None + ) -> ModuleSpec | None: + fullname_path = ( + self.tar_file_path / self.sub_path / "/".join(fullname.split(".")) + ) + if fullname_path.absolute().exists() and fullname_path.absolute().is_dir(): + spec = ModuleSpec( + fullname, + TarGzPluginLoader( + fullname, + str(fullname_path.absolute()), + self.tar_file_path.absolute(), + ), + origin=path, + is_package=True, + ) + + spec.has_location = True + elif Path(str(fullname_path.absolute()) + ".py").exists(): + spec = ModuleSpec( + fullname, + TarGzPluginLoader( + fullname, + str((str(fullname_path.absolute()) + ".py")), + self.tar_file_path.absolute(), + ), + origin=path, + is_package=False, + ) + + spec.has_location = True + else: + return None + return spec + + def find_spec( + self, fullname: str, path: list[str] | None | str, target=None + ) -> ModuleSpec | None: if isinstance(path, list): path = path[0] if len(path) > 0 else None + if self.tar_file_path.is_file() and tarfile.is_tarfile(self.tar_file_path): + return self._find_spec_targz(fullname, path, target) + elif self.tar_file_path.is_dir(): + return self._find_spec_folder(fullname, path, target) + return None + + def _find_spec_targz( + self, fullname: str, path: str | None, target=None + ) -> ModuleSpec | None: + fullname_path = self.sub_path / "/".join(fullname.split(".")) + with tarfile.open(self.tar_file_path, "r:gz") as f: names = f.getnames() if any(Path(name).is_relative_to(fullname_path) for name in names): spec = ModuleSpec( fullname, - TarGzLoader( + TarGzPluginLoader( fullname, str(self.tar_file_path / fullname_path), self.tar_file_path, @@ -339,7 +387,7 @@ def find_spec(self, fullname: str, path, target=None) -> ModuleSpec | None: elif (str(fullname_path).replace("\\", "/") + ".py") in names: spec = ModuleSpec( fullname, - TarGzLoader( + TarGzPluginLoader( fullname, str(self.tar_file_path / (str(fullname_path) + ".py")), self.tar_file_path, diff --git a/openpluginloader/manager.py b/openpluginloader/manager.py index 507be45..375adc1 100644 --- a/openpluginloader/manager.py +++ b/openpluginloader/manager.py @@ -103,10 +103,10 @@ def load_plugin(self, plugin: PluginMetadata): Important: Ensure you run `.initialize_hooks()` first """ - if plugin.min_api_version < self.api_version: - raise PluginOutOfDate(plugin, self.api_version) - if plugin.max_api_version > self.api_version: - raise PluginTooNew(plugin, self.api_version) + if plugin.min_api_version > self.api_version: + raise PluginOutOfDate(plugin, self.api_version, plugin.min_api_version) + if plugin.max_api_version < self.api_version: + raise PluginTooNew(plugin, self.api_version, plugin.max_api_version) return self.loading_strategy.load_plugin(plugin) diff --git a/other_file.py b/other_file.py new file mode 100644 index 0000000..c195725 --- /dev/null +++ b/other_file.py @@ -0,0 +1 @@ +print("We aren't supposed to be able to get here") From d384c33e431ab78e3902440b72d609daef439e8e Mon Sep 17 00:00:00 2001 From: Abigail Austin <38941820+ArachnidAbby@users.noreply.github.com> Date: Sun, 6 Sep 2026 14:08:10 -0400 Subject: [PATCH 2/3] rearrange import hooks --- openpluginloader/defaultstrategy.py | 99 +++++++++++++++-------------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/openpluginloader/defaultstrategy.py b/openpluginloader/defaultstrategy.py index 2a13531..5749e26 100644 --- a/openpluginloader/defaultstrategy.py +++ b/openpluginloader/defaultstrategy.py @@ -282,8 +282,10 @@ def exec_module(self, module: ModuleType): with clear_module_caches(DEFAULT_MODS): with set_meta_paths( [ - TarGzImportHook(self.tar_file_path, Path("site-packages")), - TarGzImportHook(self.tar_file_path, Path()), + TarGZPluginFileImportHook( + self.tar_file_path, Path("site-packages") + ), + TarGZPluginFileImportHook(self.tar_file_path, Path()), *( metapath for metapath in sys.meta_path @@ -316,6 +318,20 @@ def __init__(self, tar_file_path: Path, sub_path: Path): self.tar_file_path = tar_file_path self.sub_path = sub_path + def make_module_spec( + self, fullname: str, file_path: str, path: str | None, is_package: bool + ): + return ModuleSpec( + fullname, + TarGzLoader( + fullname, + file_path, + self.tar_file_path, + ), + origin=path, + is_package=is_package, + ) + def _find_spec_folder( self, fullname: str, path: str | None, target=None ) -> ModuleSpec | None: @@ -323,28 +339,14 @@ def _find_spec_folder( self.tar_file_path / self.sub_path / "/".join(fullname.split(".")) ) if fullname_path.absolute().exists() and fullname_path.absolute().is_dir(): - spec = ModuleSpec( - fullname, - TarGzPluginLoader( - fullname, - str(fullname_path.absolute()), - self.tar_file_path.absolute(), - ), - origin=path, - is_package=True, + spec = self.make_module_spec( + fullname, str(fullname_path.absolute()), path, True ) spec.has_location = True elif Path(str(fullname_path.absolute()) + ".py").exists(): - spec = ModuleSpec( - fullname, - TarGzPluginLoader( - fullname, - str((str(fullname_path.absolute()) + ".py")), - self.tar_file_path.absolute(), - ), - origin=path, - is_package=False, + spec = self.make_module_spec( + fullname, str((str(fullname_path.absolute()) + ".py")), path, False ) spec.has_location = True @@ -372,28 +374,17 @@ def _find_spec_targz( with tarfile.open(self.tar_file_path, "r:gz") as f: names = f.getnames() if any(Path(name).is_relative_to(fullname_path) for name in names): - spec = ModuleSpec( - fullname, - TarGzPluginLoader( - fullname, - str(self.tar_file_path / fullname_path), - self.tar_file_path, - ), - origin=path, - is_package=True, + spec = self.make_module_spec( + fullname, str(self.tar_file_path / fullname_path), path, True ) spec.has_location = True elif (str(fullname_path).replace("\\", "/") + ".py") in names: - spec = ModuleSpec( + spec = self.make_module_spec( fullname, - TarGzPluginLoader( - fullname, - str(self.tar_file_path / (str(fullname_path) + ".py")), - self.tar_file_path, - ), - origin=path, - is_package=False, + str(self.tar_file_path / (str(fullname_path) + ".py")), + path, + False, ) spec.has_location = True @@ -402,7 +393,29 @@ def _find_spec_targz( return spec -class TarGzPluginImportHook: +class TarGZPluginFileImportHook(TarGzImportHook): + """Actually in charge of loading individual files inside of a plugin.""" + + def make_module_spec( + self, + fullname: str, + file_path: str, + path: str | None, + is_package: bool, + ): + return ModuleSpec( + fullname, + TarGzPluginLoader( + fullname, + file_path, + self.tar_file_path, + ), + origin=path, + is_package=is_package, + ) + + +class DefaultPluginImportHook: """An import hook that interprets any modules prefixed as `plugin.` as a plugin within whatever plugin path is being used """ @@ -435,12 +448,6 @@ def __init__( ) self.plugins_module.has_location = True - def find_module( - self, - fullname, - path, - ): ... - def create_plugin_module( self, plugin_meta: PluginMetadata, fullname: str ) -> ModuleSpec: @@ -537,7 +544,7 @@ def create_default_manager( plugin_scanner=scanner, api_version=api_version, import_hooks=[ - TarGzPluginImportHook( + DefaultPluginImportHook( plugin_path, scanner.get_available_plugins(api_version) ), ], @@ -558,5 +565,5 @@ def create_default_manager( "PluginFolderLoader", # # finders "TarGzImportHook", - "TarGzPluginImportHook", + "DefaultPluginImportHook", ] From 94837128ceb715f708c7eaaa8ac24ba28989f90e Mon Sep 17 00:00:00 2001 From: Abigail Austin <38941820+ArachnidAbby@users.noreply.github.com> Date: Sun, 6 Sep 2026 14:08:33 -0400 Subject: [PATCH 3/3] bump --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2e98cd7..5e42b14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openpluginloader" -version = "1.0.3" +version = "1.0.4" description = "A plugin loader library meant for wide use and quick adoption." readme = "README.md" requires-python = ">=3.13"