From: Daniele Varrazzo Date: Tue, 9 Apr 2024 22:13:18 +0000 (+0200) Subject: refactor(pipeline): use Capabilities to implement is_supported X-Git-Tag: 3.2.0~45^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=137ea9964bd3f5817432022df949016df4e506ac;p=thirdparty%2Fpsycopg.git refactor(pipeline): use Capabilities to implement is_supported --- diff --git a/psycopg/psycopg/__init__.py b/psycopg/psycopg/__init__.py index 57d4243fc..6d8661aab 100644 --- a/psycopg/psycopg/__init__.py +++ b/psycopg/psycopg/__init__.py @@ -21,7 +21,7 @@ from ._pipeline import Pipeline, AsyncPipeline from .connection import Connection from .transaction import Rollback, Transaction, AsyncTransaction from .cursor_async import AsyncCursor -from ._capabilities import Capabilities +from ._capabilities import Capabilities, capabilities from .server_cursor import AsyncServerCursor, ServerCursor from .client_cursor import AsyncClientCursor, ClientCursor from .raw_cursor import AsyncRawCursor, RawCursor @@ -41,9 +41,6 @@ logger = logging.getLogger("psycopg") if logger.level == logging.NOTSET: logger.setLevel(logging.WARNING) -# A global object to check for capabilities. -capabilities = Capabilities() - # DBAPI compliance connect = Connection.connect apilevel = "2.0" @@ -74,6 +71,8 @@ __all__ = [ "AsyncServerCursor", "AsyncTransaction", "BaseConnection", + "Capabilities", + "capabilities", "ClientCursor", "Column", "Connection", diff --git a/psycopg/psycopg/_capabilities.py b/psycopg/psycopg/_capabilities.py index f222f61e6..0054b9a70 100644 --- a/psycopg/psycopg/_capabilities.py +++ b/psycopg/psycopg/_capabilities.py @@ -100,3 +100,7 @@ class Capabilities: return f"the psycopg[binary] package version {version}" else: return "system libraries" + + +# The object that will be exposed by the module. +capabilities = Capabilities() diff --git a/psycopg/psycopg/_pipeline.py b/psycopg/psycopg/_pipeline.py index 05d0beb64..7c6ce5c82 100644 --- a/psycopg/psycopg/_pipeline.py +++ b/psycopg/psycopg/_pipeline.py @@ -16,6 +16,7 @@ from .pq.misc import connection_summary from ._encodings import pgconn_encoding from ._preparing import Key, Prepare from .generators import pipeline_communicate, fetch_many, send +from ._capabilities import capabilities if TYPE_CHECKING: from .pq.abc import PGresult @@ -63,36 +64,12 @@ class BasePipeline: def is_supported(cls) -> bool: """Return `!True` if the psycopg libpq wrapper supports pipeline mode.""" if BasePipeline._is_supported is None: - BasePipeline._is_supported = not cls._not_supported_reason() + BasePipeline._is_supported = capabilities.has_pipeline() return BasePipeline._is_supported - @classmethod - def _not_supported_reason(cls) -> str: - """Return the reason why the pipeline mode is not supported. - - Return an empty string if pipeline mode is supported. - """ - # Support only depends on the libpq functions available in the pq - # wrapper, not on the database version. - if pq.version() < 140000: - return ( - f"libpq too old {pq.version()};" - " v14 or greater required for pipeline mode" - ) - - if pq.__build_version__ < 140000: - return ( - f"libpq too old: module built for {pq.__build_version__};" - " v14 or greater required for pipeline mode" - ) - - return "" - def _enter_gen(self) -> PQGen[None]: - if not self.is_supported(): - raise e.NotSupportedError( - f"pipeline mode not supported: {self._not_supported_reason()}" - ) + if not self._is_supported: + capabilities.has_pipeline(check=True) if self.level == 0: self.pgconn.enter_pipeline_mode() elif self.command_queue or self.pgconn.transaction_status == ACTIVE: diff --git a/tests/fix_db.py b/tests/fix_db.py index 37ee7ac32..5f2a901eb 100644 --- a/tests/fix_db.py +++ b/tests/fix_db.py @@ -69,9 +69,11 @@ def pytest_collection_modifyitems(items): def pytest_runtest_setup(item): - for m in item.iter_markers(name="pipeline"): - if not psycopg.Pipeline.is_supported(): - pytest.skip(psycopg.Pipeline._not_supported_reason()) + try: + psycopg.capabilities.has_pipeline(check=True) + except psycopg.NotSupportedError as ex: + for m in item.iter_markers(name="pipeline"): + pytest.skip(str(ex)) def pytest_configure(config): @@ -213,8 +215,10 @@ def conn(conn_cls, dsn, request, tracefile): @pytest.fixture(params=[True, False], ids=["pipeline=on", "pipeline=off"]) def pipeline(request, conn): if request.param: - if not psycopg.Pipeline.is_supported(): - pytest.skip(psycopg.Pipeline._not_supported_reason()) + try: + psycopg.capabilities.has_pipeline(check=True) + except psycopg.NotSupportedError as ex: + pytest.skip(str(ex)) with conn.pipeline() as p: yield p return @@ -236,8 +240,10 @@ async def aconn(dsn, aconn_cls, request, tracefile): @pytest.fixture(params=[True, False], ids=["pipeline=on", "pipeline=off"]) async def apipeline(request, aconn): if request.param: - if not psycopg.Pipeline.is_supported(): - pytest.skip(psycopg.Pipeline._not_supported_reason()) + try: + psycopg.capabilities.has_pipeline(check=True) + except psycopg.NotSupportedError as ex: + pytest.skip(str(ex)) async with aconn.pipeline() as p: yield p return diff --git a/tests/pq/test_pq.py b/tests/pq/test_pq.py index 076c3b62b..9a2b47334 100644 --- a/tests/pq/test_pq.py +++ b/tests/pq/test_pq.py @@ -54,4 +54,4 @@ def test_pipeline_not_supported(conn): with conn.pipeline(): pass - assert "too old" in str(exc.value) + assert "requires libpq version 14.0 or newer" in str(exc.value) diff --git a/tests/test_generators.py b/tests/test_generators.py index 8397c203b..9bb8bb1af 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -174,8 +174,10 @@ def test_pipeline_communicate_abort(pgconn, pipeline_demo, pipeline, generators) @pytest.fixture def pipeline_uniqviol(pgconn): - if not psycopg.Pipeline.is_supported(): - pytest.skip(psycopg.Pipeline._not_supported_reason()) + try: + psycopg.capabilities.has_pipeline(check=True) + except psycopg.NotSupportedError as ex: + pytest.skip(str(ex)) assert pgconn.pipeline_status == 0 res = pgconn.exec_(b"DROP TABLE IF EXISTS pg_pipeline_uniqviol") assert res.status == pq.ExecStatus.COMMAND_OK, res.error_message