Looking at the docs this seems the default value when an empty string is
specified, as opposite as the value in the PGPORT env var. See
<https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-PORT>.
This change is an improvement on top of
40dfa0b, where we allowed PGport
to return NULL. This is likely a libpq bug (reported in
<https://www.postgresql.org/message-id/CA%2Bmi_8YTS8WPZPO0PAb2aaGLwHuQ0DEQRF0ZMnvWss4y9FwDYQ%40mail.gmail.com>)
but now it's in the wild so we better support it rather than dying of
assert or by int()'ing an empty string.
- Don't process further connection attempts after Ctrl-C (:ticket:`#1077`).
- Fix cursors to correctly iterate over rows even if their row factory
returns `None` (:ticket:`#1073`).
+- Fix `ConnectionInfo.port` when the port is specified as an empty string
+ (:ticket:`#1078`).
Current release
from pathlib import Path
from datetime import tzinfo
+from . import errors as e
from . import pq
from ._tz import get_tzinfo
from .conninfo import make_conninfo
@property
def port(self) -> int:
"""The port of the active connection. See :pq:`PQport()`."""
- return int(self._get_pgconn_attr("port"))
+ if sport := self._get_pgconn_attr("port"):
+ return int(sport)
+ return self._get_compiled_port()
+
+ def _get_compiled_port(sef) -> int:
+ # As per docs: an empty string means the build default, not e.g.
+ # something configured by PGPORT
+ # https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-PORT
+ for info in pq.Conninfo().get_defaults():
+ if info.keyword == b"port":
+ if info.compiled:
+ return int(info.compiled)
+ break
+
+ raise e.InternalError("couldn't find the connection port")
@property
def dbname(self) -> str:
conn.info.port
+@pytest.mark.skipif(psycopg.pq.__impl__ != "python", reason="can't monkeypatch C")
+def test_blank_port(conn, monkeypatch):
+ monkeypatch.setenv("PGPORT", "9999")
+ monkeypatch.setattr(
+ psycopg.pq._pq_ctypes, "PQport", lambda self: b"" # type: ignore[attr-defined]
+ )
+ assert conn.pgconn.port == b""
+ # assume 5432 is the compiled value
+ assert conn.info.port == 5432
+
+
def test_get_params(conn, dsn):
info = conn.info.get_parameters()
for k, v in conninfo_to_dict(dsn).items():