Psycopg 3.2 makes client capabilities an explicit dependency
Psycopg 3.2 adds safer cancellation APIs and native PostgreSQL placeholders. Some capabilities depend on libpq 17, which the July binary packages do not yet bundle.
Installing a new Python driver does not necessarily provide every capability its API can express. The July 1 announcement of Psycopg 3.2 makes that dependency explicit. Several additions require libpq 17, while PostgreSQL 17 has not yet reached its final release. The binary packages do not yet bundle that client library, so those additions need a development libpq and the Python or C source distribution.
This is particularly relevant to cancellation. Psycopg 3.2 adds support for encrypted, nonblocking cancellation through new libpq functions. A method name in the Python package alone cannot establish which underlying implementation will run. The release also introduces a capabilities object so application code can inspect what the loaded client library supports.
I prefer PostgreSQL tooling that makes such dependencies visible. The database's behavior is already a substantial part of an application's contract. Knowing which client-library operation implements cancellation makes that contract more precise, particularly when an asynchronous interface would otherwise suggest that every operation avoids blocking.
Cancellation has its own implementation requirements
The versioned connection documentation describes shortcomings in the older cancel() method. It uses libpq's PQcancel, which blocks even on an asynchronous connection. It may also use an insecure connection even when the original connection was secure. The security properties of the main query connection therefore do not automatically describe that cancellation path.
Version 3.2 adds cancel_safe(), with support for encrypted and nonblocking cancellation when the relevant libpq functionality is available. The documentation recommends using that method where possible. It also warns that its timeout has no effect with libpq older than 17. The effective behavior depends on a capability of the client library loaded by the process.
There is an explicit check for this dependency, Capabilities.has_cancel_safe. The documentation says it can establish whether cancel_safe() will avoid falling back to legacy libpq functions. That is more informative than checking the Python package version alone. Two environments can expose the same Python method while loading client libraries with different supported operations.
The older method retains a specific use. The documentation says cancel() is safe for use as a Python signal handler, while cancel_safe() should not be used that way. Replacing every call mechanically would therefore erase a documented constraint. The choice depends on where the cancellation originates as well as on the library capabilities available to execute it.
The 3.2 release notes say Psycopg uses the safer method internally where possible for keyboard interrupts and COPY termination. The qualification matters. It connects a user-facing interruption to the new implementation without promising that an older libpq suddenly supplies the new cancellation functions.
Native placeholders change how query text is interpreted
Another addition is the raw-query cursor family. The tagged cursor documentation describes RawCursor and its asynchronous counterpart as accepting PostgreSQL placeholders such as $1 and $2. Ordinary Psycopg cursor queries use %s placeholders. Native placeholders are useful for complex query text that itself contains %s, including inside quoted strings.
The cursor choice declares which placeholder convention applies. The following example follows the documented factory pattern and assumes that dsn contains the application's connection string.
from psycopg import connect, RawCursor
with connect(dsn, cursor_factory=RawCursor) as conn:
with conn.cursor() as cur:
cur.execute("SELECT $1, $2", [1, "Hello"])
row = cur.fetchone()
The parameters still travel through a parameter-binding interface. The word raw here does not instruct the application to interpolate values into SQL. It selects PostgreSQL's placeholder syntax. The documentation limits raw-query cursor arguments to positional lists or tuples, so a mapping of named parameters is not an interchangeable input.
There is also a distinction between binding and where result rows reside. RawCursor returns the complete result to the client, as Cursor does. RawServerCursor and its asynchronous counterpart provide server-side cursors with native placeholders. Selecting raw placeholders alone does not turn complete result retrieval into a server-side streaming operation.
That distinction is useful because cursor terminology can describe more than one decision. The normal client-side cursor sends query text and parameters separately but receives its result set into client memory. A client-binding cursor instead combines query text and values on the client. The new raw classes concern the placeholder convention while retaining the documented binding and result-handling behavior of their corresponding cursor types.
I find the explicit cursor classes preferable to inferring those choices from a query string. An application can state which convention it expects at cursor construction. The SQL and its parameter container then have a documented relationship, including the restriction against named dictionaries for raw-query cursors.
Smaller additions remove conversion work
Psycopg 3.2 also adds support for NumPy integer, floating-point and boolean scalar types. That expands the values the driver can adapt directly. It is a scalar-type addition, and should not be described as a blanket promise that every NumPy object is now a PostgreSQL parameter. The release notes identify the supported categories specifically.
Notification reception gains timeout and stop_after parameters on Connection.notifies(). The connection documentation places this method in the existing LISTEN and NOTIFY workflow. The new parameters give reception an explicit stopping condition without changing the database's notification commands into a different messaging system.
Pipeline mode receives a compatibility correction as well. A cursor now preserves only the result set of its last execution, consistently with behavior outside pipeline mode. Code that depended on collecting multiple results from the same cursor needs to account for that documented change.
The release also adds scalar_row, a row factory that returns scalar values from a query. Together these changes reduce some application-side handling around existing PostgreSQL operations. They have a different dependency story from the new libpq 17 APIs, which is why the announcement's packaging qualification should accompany any account of the release.
As of July 1, source-based installations with a development libpq 17 can use capabilities absent from the announced binary packages. The project plans packages bundling libpq 17 after PostgreSQL 17's official release. Until then, the loaded client library remains necessary information when describing which Psycopg 3.2 operations an environment can actually perform.