]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
docs: add capabilities documentation
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 9 Apr 2024 21:41:28 +0000 (23:41 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 12 Apr 2024 22:09:08 +0000 (00:09 +0200)
docs/advanced/pipeline.rst
docs/advanced/prepare.rst
docs/api/connections.rst
docs/api/module.rst
docs/api/objects.rst
docs/news.rst
psycopg/psycopg/_capabilities.py
tests/test_capabilities.py

index a56049ccbd28f0fc0012563a00b4e3c2617cf409..cf71e25091651ce310591a3efcc10a2ea2ab565b 100644 (file)
@@ -302,5 +302,5 @@ The fine prints
 
 The pipeline mode is available on any currently supported PostgreSQL version,
 but, in order to make use of it, the client must use a libpq from PostgreSQL
-14 or higher. You can use `Pipeline.is_supported()` to make sure your client
-has the right library.
+14 or higher. You can use the `~Capabilities.has_pipeline` capability to make
+sure your client has the right library.
index 25f98a51c39b6fbb99a83ecdaca549111c3eafe9..e376e8e053a0cd592323e2f4c3629613c4c90428 100644 (file)
@@ -65,9 +65,11 @@ Using prepared statements with PgBouncer
 Starting from 3.2, Psycopg supports prepared statements when using the
 PgBouncer__ middleware, using the following caveats:
 
-- PgBouncer version must be at least version `1.22`__.
+- PgBouncer version must be version `1.22`__ or newer.
 - PgBouncer `max_prepared_statements`__ must be greater than 0.
-- The libpq version on the client must be from PostgreSQL 17 or higher.
+- The libpq version on the client must be from PostgreSQL 17 or newer
+  (you can check the `~Capabilities.has_pgbouncer_prepared` capability to
+  verify it).
 
 .. __: https://www.pgbouncer.org/
 .. __: https://www.pgbouncer.org/2024/01/pgbouncer-1-22-0
index 1aaeac082da04f6a1dc09a57c358016aab14c396..a6d95d20b22d9f443a77627c680b52f0ce31282e 100644 (file)
@@ -287,6 +287,11 @@ The `!Connection` class
 
     .. automethod:: cancel_safe
 
+        .. note::
+
+            You can use the `~Capabilities.has_cancel_safe` capability to check
+            if `!cancel_safe()` will not fall back on the legacy implementation.
+
         .. warning::
 
             This method shouldn't be used as a `~signal.signal` handler.
index 3c3d3c43b3cd5f0d77c401bb56ac8dca8a034243..a8c1625afa681bec8c7decd5fc222f94f61e0a18 100644 (file)
@@ -17,6 +17,25 @@ it also exposes the `module-level objects`__ required by the specifications.
    If you need an asynchronous connection use `AsyncConnection.connect`
    instead.
 
+.. data:: capabilities
+
+    An object that can be used to verify that the client library used by
+    psycopg implements a certain feature. For instance::
+
+        # Fail at import time if encrypted passwords is not available
+        import psycopg
+        psycopg.capabilities.has_encrypt_password(check=True)
+
+        # Verify at runtime if a feature can be used
+        if psycopg.capabilities.has_hostaddr():
+            print(conn.info.hostaddr)
+        else:
+            print("unknown connection hostadd")
+
+    :type: `Capabilities`
+
+    .. versionadded:: 3.2
+
 
 .. rubric:: Exceptions
 
index f085ed9a96b77c21ecfb2c21dd3299d929206a26..3b285daf76bec201661149a67abeea993f658ca9 100644 (file)
@@ -74,8 +74,9 @@ Connection information
 
     .. autoattribute:: hostaddr
 
-        Only available if the libpq used is at least from PostgreSQL 12.
-        Raise `~psycopg.NotSupportedError` otherwise.
+        Only available if the libpq used is from PostgreSQL 12 or newer.
+        Raise `~psycopg.NotSupportedError` otherwise. You can use the
+        `~Capabilities.has_hostaddr` capability to check for support.
 
     .. autoattribute:: port
     .. autoattribute:: dbname
@@ -109,6 +110,58 @@ Connection information
             .. __: https://www.postgresql.org/docs/current/multibyte.html
 
 
+.. _capabilities:
+
+Libpq capabilities information
+------------------------------
+
+.. autoclass:: Capabilities
+
+    An instance of this object is normally exposed by the module as the object
+    `psycopg.capabilities`.
+
+    Every feature check is implemented by an `!has_SOMETHING()` method. All
+    the methods return a boolean value stating if the capability is supported,
+    which can be used by a program to degrade gracefully::
+
+        if psycopg.capabilities.has_pipeline()
+            with conn.pipeline():
+                operations(conn)
+        else:
+            logger.warning("slower")
+            operations(conn)
+
+    If `check` is `!True`, and the capability is not supported, raise a
+    `NotSupportedError` instead of returning `!False`, explaining why the
+    feature is not supported. This allows to make a check at import time,
+    crashing early and with a clear description of the problem.
+
+        >>> import psycopg
+        >>> psycopg.capabilities.has_pipeline(check=True)
+        Traceback (most recent call last):
+          ...
+        psycopg.NotSupportedError: the feature 'Connection.pipeline()' is not available:
+            the client libpq version (imported from system libraries) is 13.4; the
+            feature requires libpq version 14.0 or newer
+
+    .. versionadded:: 3.2
+
+    .. automethod:: has_encrypt_password
+    .. automethod:: has_hostaddr
+    .. automethod:: has_pipeline
+    .. automethod:: has_set_trace_flags
+    .. automethod:: has_cancel_safe
+
+        .. note::
+
+            The `!cancel_safe()` method is implemented anyway, but it will use
+            the legacy :pq:`PQcancel` implementation.
+
+    .. automethod:: has_pgbouncer_prepared
+
+        .. seealso:: :ref:`pgbouncer`
+
+
 The description `Column` object
 -------------------------------
 
index b4e3586ac277c1b0868f0784005b72e0bd293650..af3f2e32cc55256e8589c661da56b7e16ae598ad 100644 (file)
@@ -19,6 +19,8 @@ Psycopg 3.2 (unreleased)
   (:ticket:`340`).
 - Add :ref:`raw-query-cursors` to execute queries using placeholders in
   PostgreSQL format (`$1`, `$2`...) (:ticket:`#560`).
+- Add `psycopg.capabilities` object to :ref:`inspect the libpq capabilities
+  <capabilities>` (:ticket:`#772`).
 - Add `~rows.scalar_row` to return scalar values from a query (:ticket:`#723`).
 - Prepared statements are now :ref:`compatible with PgBouncer <pgbouncer>`.
   (:ticket:`#589`).
index 6b67f49f12a31153d2570b213d359fbcd93292d0..f222f61e6d82e63aa0d0b8fc59f6d31c4fa595ce 100644 (file)
@@ -11,36 +11,49 @@ from .errors import NotSupportedError
 
 class Capabilities:
     """
-    Check if a feature is supported.
-
-    Every feature check is implemented by a check method `has_SOMETHING`.
-    All the methods return a boolean stating if the capability is supported.
-    If not supported and `check` is True, raise a `NotSupportedError` instead
-    explaining why the feature is not supported.
+    An object to check if a feature is supported by the libpq available on the client.
     """
 
     def has_encrypt_password(self, check: bool = False) -> bool:
-        """Check if the `~PGconn.encrypt_password()` method is implemented."""
-        return self._has_feature("PGconn.encrypt_password()", 100000, check=check)
+        """Check if the `PGconn.encrypt_password()` method is implemented.
+
+        The feature requires libpq 10.0 and greater.
+        """
+        return self._has_feature("pq.PGconn.encrypt_password()", 100000, check=check)
 
     def has_hostaddr(self, check: bool = False) -> bool:
-        """Check if the `~ConnectionInfo.hostaddr` attribute is implemented."""
-        return self._has_feature("Connection.pipeline()", 120000, check=check)
+        """Check if the `ConnectionInfo.hostaddr` attribute is implemented.
+
+        The feature requires libpq 12.0 and greater.
+        """
+        return self._has_feature("Connection.info.hostaddr", 120000, check=check)
 
     def has_pipeline(self, check: bool = False) -> bool:
-        """Check if the `~Connection.pipeline()` method is implemented."""
+        """Check if the :ref:`pipeline mode <pipeline-mode>` is supported.
+
+        The feature requires libpq 14.0 and greater.
+        """
         return self._has_feature("Connection.pipeline()", 140000, check=check)
 
-    def has_set_trace_flag(self, check: bool = False) -> bool:
-        """Check if the `~PGconn.set_trace_flag()` method is implemented."""
-        return self._has_feature("PGconn.set_trace_flag()", 140000, check=check)
+    def has_set_trace_flags(self, check: bool = False) -> bool:
+        """Check if the `pq.PGconn.set_trace_flags()` method is implemented.
+
+        The feature requires libpq 14.0 and greater.
+        """
+        return self._has_feature("PGconn.set_trace_flags()", 140000, check=check)
 
     def has_cancel_safe(self, check: bool = False) -> bool:
-        """Check if the `Connection.cancel_safe()` method is implemented."""
+        """Check if the `Connection.cancel_safe()` method is implemented.
+
+        The feature requires libpq 17.0 and greater.
+        """
         return self._has_feature("Connection.cancel_safe()", 170000, check=check)
 
     def has_pgbouncer_prepared(self, check: bool = False) -> bool:
-        """Check if prepared statements in PgBouncer are supported."""
+        """Check if prepared statements in PgBouncer are supported.
+
+        The feature requires libpq 17.0 and greater.
+        """
         return self._has_feature(
             "PgBouncer prepared statements compatibility", 170000, check=check
         )
index b0e3c2be51a9cbca04d0867a5f7f6b313284c773..6a3a2da9394600109436eb8651a403a5a768446c 100644 (file)
@@ -6,10 +6,10 @@ from psycopg import pq, _cmodule
 from psycopg import capabilities, NotSupportedError
 
 caps = [
-    ("has_encrypt_password", "encrypt_password", 10),
-    ("has_hostaddr", "PGconn.hostaddr", 12),
+    ("has_encrypt_password", "pq.PGconn.encrypt_password()", 10),
+    ("has_hostaddr", "Connection.info.hostaddr", 12),
     ("has_pipeline", "Connection.pipeline()", 14),
-    ("has_set_trace_flag", "PGconn.set_trace_flag()", 14),
+    ("has_set_trace_flags", "PGconn.set_trace_flags()", 14),
     ("has_cancel_safe", "Connection.cancel_safe()", 17),
     ("has_pgbouncer_prepared", "PgBouncer prepared statements compatibility", 17),
 ]