Lanzamiento de Psycopg 3.3
¡Ya está disponible Psycopg 3.3, y de verdad vale la pena emocionarse!
Consultas con template strings
Esta versión incorpora una de las grandes novedades de Python 3.14: las template strings, que permiten escribir consultas SQL claras, seguras y muy expresivas.
Por ejemplo:
def fetch_person(conn, name): # 'name' will be handled safely: as a server-side parameter or # correctly quoted and escaped if client-side binding is required cur = conn.execute(t"SELECT * FROM people WHERE name = {name}") return cur.fetchone()
La sintaxis es la misma que la de las f-strings, introducidas en el venerable Python 3.6 (¿quizás la característica que finalmente acabó con Python 2?), pero ahora combinadas con la seguridad y flexibilidad de adaptación de Psycopg 3. Las template strings también ayudan a generar SQL dinámico de forma mucho más concisa que con el módulo psycopg.sql:
def delete_something(conn, table_name, name): # Mixing client-side query composition with server-side parameters binding conn.execute(t"DELETE FROM {table_name:i} WHERE name = {name}") # Composing non-parametric statements entirely client-side conn.execute(t"NOTIFY {table_name + '.deleted':i}, {name:l}")
Consulten la documentación completa de soporte para t-strings para inspirarse.
Adaptación de compuestos más flexible
Antes, solo era posible adaptar tipos compuestos de PostgreSQL a tipos secuenciales de Python con un mapeo 1:1 estricto respecto a los campos del tipo en la base de datos.
Ahora hemos añadido flexibilidad adicional: podemos personalizar tanto la creación de objetos Python genéricos (incluyendo aquellos que solo aceptan argumentos con palabras clave) como la extracción de secuencias de atributos de objetos no secuenciales… ¿quizás dataclasses?
from dataclasses import dataclass from psycopg.types.composite import CompositeInfo, register_composite @dataclass class MiniPerson: age: int name: str height: float | None = None @classmethod def from_db(cls, seq, info): return cls(name=seq[0], age=seq[1]) def to_db(self, info): return [self.name, self.age] conn.execute("CREATE TYPE mini_person AS (name text, age int)") info = CompositeInfo.fetch(conn, "mini_person") register_composite( info, conn, factory=MiniPerson, make_object=MiniPerson.from_db, make_sequence=MiniPerson.to_db) conn.execute("SELECT ('John', 33)::mini_person").fetchone()[0] # MiniPerson(age=33, name='John', height=None) conn.execute( "SELECT (%(person)s).name || ' next year will be ' || (%(person)s).age + 1", {"person": MiniPerson(name="John", age=33)}, ).fetchone()[0] # 'John next year will be 34'
Adiós al problema de ‘fetchone()’ con los type checkers
Cuando se emplean Mypy u otros type checkers (comprobadores de tipos) junto a Psycopg, es probable encontrar falsos positivos en las llamadas a fetchone(). Pese a la certeza absoluta de que una consulta devolverá un registro, la anotación de tipo para fetchone() indica que puede devolver None; en consecuencia, los type checkers generan advertencias ante patrones como:
cur.execute("SELECT count(*) FROM my_table") # Always returns exactly one value count = cur.fetchone()[0] # Error: value of type "tuple | None" is not indexable
En Psycopg 3.3, el cursor se convierte en un iterator, mientras que antes era solo un iterable. La diferencia es sutil pero importante: un iterador mantiene su propio estado de iteración y no necesita crear un nuevo objeto en cada pasada.
Más importante aún, este cambio significa que es posible usar next() o anext() para obtener un registro — y estas funciones nunca devuelven None. Mypy lo agradece, y es probable que ustedes también:
cur.execute("SELECT count(*) FROM my_table") count = next(cur)[0]
Mejoras en los pools de conexiones
Los parámetros de un pool de conexiones ahora pueden cambiarse dinámicamente — útil, por ejemplo, para admitir tokens de acceso de corta duración usados como contraseñas, solicitados por algunos proveedores de bases de datos en la nube.
Además, ahora está disponible un útil método drain() para recrear todas las conexiones del pool. Esto resulta práctico cuando es necesario inspeccionar la base de datos para encontrar los OIDs de tipos de extensión que se registrarán: sin drenar el pool, las conexiones ya existentes quedarían obsoletas después de configurar los adaptadores.
…¡Y más!
Otras mejoras incluyen mayor flexibilidad al navegar resultados después de fetchmany() o de sentencias que devuelven múltiples conjuntos de resultados, la capacidad de reconfigurar cargadores después de ejecutar una consulta, y muchos otros ajustes más. Pueden encontrar la lista completa en las notas de la versión de psycopg y en las notas de la versión del pool.
¡Su ayuda es importante!
Psycopg es el estándar de facto para la comunicación entre Python y PostgreSQL — dos componentes fundamentales que impulsan incontables negocios e infraestructura crítica. Mantener esta biblioteca con los más altos niveles de fiabilidad, rendimiento y seguridad requiere mucho cuidado y trabajo continuo.
Si están usando Python y PostgreSQL y quieren ayudar a asegurar que la interfaz entre ambos siga siendo robusta y mejorando, incorporando nuevas funciones del lenguaje y de la base de datos, por favor consideren apoyar el proyecto 💜
Muchas gracias, y ¡que lo disfruten!

