From 974fe2f82f06a9d3f9b7155f5f1aa941dd3b69a6 Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Sat, 7 Mar 2026 18:33:04 -0600 Subject: [PATCH 1/2] fix: defer plugin entrypoint loading until runtime Fixes simonw/sqlite-utils#713 --- sqlite_utils/cli.py | 3 ++- sqlite_utils/db.py | 3 ++- sqlite_utils/plugins.py | 9 +++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index 9b9ee20e7..470d93beb 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -14,7 +14,7 @@ NoTable, quote_identifier, ) -from sqlite_utils.plugins import pm, get_plugins +from sqlite_utils.plugins import ensure_plugins_loaded, pm, get_plugins from sqlite_utils.utils import maximize_csv_field_size_limit from sqlite_utils import recipes import textwrap @@ -3264,6 +3264,7 @@ def plugins_list(): click.echo(json.dumps(get_plugins(), indent=2)) +ensure_plugins_loaded() pm.hook.register_commands(cli=cli) diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index aacdc8933..f989bedc6 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -40,7 +40,7 @@ Tuple, ) import uuid -from sqlite_utils.plugins import pm +from sqlite_utils.plugins import ensure_plugins_loaded, pm try: from sqlite_dump import iterdump # type: ignore[import-not-found] @@ -382,6 +382,7 @@ def __init__( self._registered_functions: set = set() self.use_counts_table = use_counts_table if execute_plugins: + ensure_plugins_loaded() pm.hook.prepare_connection(conn=self.conn) self.strict = strict diff --git a/sqlite_utils/plugins.py b/sqlite_utils/plugins.py index 457a90716..4b4451327 100644 --- a/sqlite_utils/plugins.py +++ b/sqlite_utils/plugins.py @@ -6,10 +6,15 @@ pm: pluggy.PluginManager = pluggy.PluginManager("sqlite_utils") pm.add_hookspecs(hookspecs) +_plugins_loaded = False -if not getattr(sys, "_called_from_test", False): - # Only load plugins if not running tests + +def ensure_plugins_loaded() -> None: + global _plugins_loaded + if _plugins_loaded or getattr(sys, "_called_from_test", False): + return pm.load_setuptools_entrypoints("sqlite_utils") + _plugins_loaded = True def get_plugins() -> List[Dict[str, Union[str, List[str]]]]: From af5e3e3a5b8da4b89b9726792ce7ea04215c3469 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sun, 21 Jun 2026 16:10:48 -0700 Subject: [PATCH 2/2] Lazy-load plugins when listing them Keep plugin entrypoints from loading at module import time, but preserve the existing behavior of get_plugins() by loading entrypoints the first time plugins are listed outside tests. --- sqlite_utils/plugins.py | 1 + tests/test_plugins.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/sqlite_utils/plugins.py b/sqlite_utils/plugins.py index 4b4451327..0aff7ffa6 100644 --- a/sqlite_utils/plugins.py +++ b/sqlite_utils/plugins.py @@ -18,6 +18,7 @@ def ensure_plugins_loaded() -> None: def get_plugins() -> List[Dict[str, Union[str, List[str]]]]: + ensure_plugins_loaded() plugins: List[Dict[str, Union[str, List[str]]]] = [] plugin_to_distinfo = dict(pm.list_plugin_distinfo()) for plugin in pm.get_plugins(): diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 1d459c992..c793e321a 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -2,6 +2,7 @@ import click import importlib import pytest +import sys from sqlite_utils import cli, Database, hookimpl, plugins @@ -16,6 +17,36 @@ def _supports_pragma_function_list(): db.close() +def test_get_plugins_loads_setuptools_entrypoints_once(monkeypatch): + calls = [] + monkeypatch.delattr(sys, "_called_from_test", raising=False) + monkeypatch.setattr(plugins, "_plugins_loaded", False) + monkeypatch.setattr( + plugins.pm, + "load_setuptools_entrypoints", + lambda group: calls.append(group) or 0, + ) + + plugins.get_plugins() + plugins.get_plugins() + + assert calls == ["sqlite_utils"] + + +def test_get_plugins_does_not_load_setuptools_entrypoints_in_tests(monkeypatch): + calls = [] + monkeypatch.setattr(sys, "_called_from_test", True, raising=False) + monkeypatch.setattr(plugins, "_plugins_loaded", False) + monkeypatch.setattr( + plugins.pm, + "load_setuptools_entrypoints", + lambda group: calls.append(group) or 0, + ) + + assert plugins.get_plugins() == [] + assert calls == [] + + def test_register_commands(): importlib.reload(cli) assert plugins.get_plugins() == []