Current release
---------------
+Psyocpg 3.3.3 (unreleased)
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Retain `Error.pgconn` when raising a single exception for multiple connection
+attempt errors (:ticket:`#1246`).
+
+
Psycopg 3.3.2
^^^^^^^^^^^^^
lines = [str(last_ex)]
lines.append("Multiple connection attempts failed. All failures were:")
lines.extend((f"- {descr}: {error}" for error, descr in conn_errors))
- raise type(last_ex)("\n".join(lines)).with_traceback(None)
+ new_ex = type(last_ex)("\n".join(lines), pgconn=last_ex.pgconn)
+ raise new_ex.with_traceback(None)
if (
capabilities.has_used_gssapi()
lines = [str(last_ex)]
lines.append("Multiple connection attempts failed. All failures were:")
lines.extend(f"- {descr}: {error}" for error, descr in conn_errors)
- raise type(last_ex)("\n".join(lines)).with_traceback(None)
+ new_ex = type(last_ex)("\n".join(lines), pgconn=last_ex.pgconn)
+ raise new_ex.with_traceback(None)
if (
capabilities.has_used_gssapi()
# DO NOT CHANGE! Change the original file instead.
from __future__ import annotations
+import os
import sys
import time
import logging
from psycopg import errors as e
from psycopg import pq
from psycopg.rows import tuple_row
-from psycopg.conninfo import conninfo_to_dict, timeout_from_conninfo
+from psycopg.conninfo import conninfo_to_dict, make_conninfo, timeout_from_conninfo
from psycopg._conninfo_utils import get_param
from .acompat import skip_async, skip_sync, sleep
)
+def test_multi_attempt_pgconn(conn_cls, dsn, monkeypatch):
+ with conn_cls.connect(dsn) as conn:
+ if not conn.pgconn.used_password:
+ pytest.skip("test connection needs no password")
+
+ monkeypatch.delenv("PGPASSWORD", raising=False)
+ info = conninfo_to_dict(dsn)
+ info.pop("password", None)
+ info["host"] = ",".join([str(info.get("host", os.environ.get("PGHOST", "")))] * 2)
+ dsn = make_conninfo("", **info)
+
+ with pytest.raises(psycopg.OperationalError, match="connection failed:") as e:
+ conn_cls.connect(dsn)
+
+ msg = str(e.value)
+ assert MULTI_FAILURE_MESSAGE in msg
+ assert e.value.pgconn
+
+
def test_connect_str_subclass(conn_cls, dsn):
class MyString(str):
from __future__ import annotations
+import os
import sys
import time
import logging
from psycopg import errors as e
from psycopg import pq
from psycopg.rows import tuple_row
-from psycopg.conninfo import conninfo_to_dict, timeout_from_conninfo
+from psycopg.conninfo import conninfo_to_dict, make_conninfo, timeout_from_conninfo
from psycopg._conninfo_utils import get_param
from .acompat import asleep, skip_async, skip_sync
assert any(expected_host2 in line and expected_error in line for line in msg_lines)
+async def test_multi_attempt_pgconn(aconn_cls, dsn, monkeypatch):
+ async with await aconn_cls.connect(dsn) as conn:
+ if not conn.pgconn.used_password:
+ pytest.skip("test connection needs no password")
+
+ monkeypatch.delenv("PGPASSWORD", raising=False)
+ info = conninfo_to_dict(dsn)
+ info.pop("password", None)
+ info["host"] = ",".join([str(info.get("host", os.environ.get("PGHOST", "")))] * 2)
+ dsn = make_conninfo("", **info)
+
+ with pytest.raises(psycopg.OperationalError, match="connection failed:") as e:
+ await aconn_cls.connect(dsn)
+
+ msg = str(e.value)
+ assert MULTI_FAILURE_MESSAGE in msg
+ assert e.value.pgconn
+
+
async def test_connect_str_subclass(aconn_cls, dsn):
class MyString(str):
pass