]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
refactor(pipeline): use Capabilities to implement is_supported
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 9 Apr 2024 22:13:18 +0000 (00:13 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 12 Apr 2024 22:09:08 +0000 (00:09 +0200)
psycopg/psycopg/__init__.py
psycopg/psycopg/_capabilities.py
psycopg/psycopg/_pipeline.py
tests/fix_db.py
tests/pq/test_pq.py
tests/test_generators.py

index 57d4243fca5221c5892502164274548e2039961d..6d8661aabb105d899f2e3ead72cc61e94a8fc7c8 100644 (file)
@@ -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",
index f222f61e6d82e63aa0d0b8fc59f6d31c4fa595ce..0054b9a70d4a57052e70337e7343242f9d90cb5e 100644 (file)
@@ -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()
index 05d0beb64409f491ccc4a57efdcc5031bfa55b83..7c6ce5c82563d563cb54d7fd9a7c34bc9ce9b5ac 100644 (file)
@@ -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:
index 37ee7ac3252d117aac3844c4318483207b40cd88..5f2a901ebf060df95ab87a9c34eea76dde157226 100644 (file)
@@ -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
index 076c3b62b9cf42a84a6c7589c085d156334a9fbb..9a2b47334cbee0f16df465661b19d7083b790653 100644 (file)
@@ -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)
index 8397c203b649212efb16b88bf3cb88707282ef4f..9bb8bb1afc635797e4b30394125ac68308fa82e6 100644 (file)
@@ -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