]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Add Connection.fileno()
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 28 Feb 2021 00:36:34 +0000 (01:36 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 28 Feb 2021 13:06:53 +0000 (14:06 +0100)
docs/connection.rst
psycopg3/psycopg3/connection.py
tests/test_concurrency.py
tests/test_concurrency_async.py
tests/test_connection.py
tests/test_connection_async.py

index a7c25f1963c3049099b78b729703f22a8548e339..c82c7bf65662f1d330b265fbe69bae1e10481ef5 100644 (file)
@@ -166,6 +166,8 @@ The `!Connection` class
         TODO
 
 
+    .. automethod:: fileno
+
     .. autoattribute:: prepare_threshold
         :annotation: Optional[int]
 
index 9c701fdd382d948a8778dfadd95b3ef79ef90294..d724cda5136f2d09915570709cbe72a3021f4b6c 100644 (file)
@@ -209,6 +209,18 @@ class BaseConnection(AdaptContext):
         # implement the AdaptContext protocol
         return self
 
+    def fileno(self) -> int:
+        """Return the file descriptor of the connection.
+
+        This function allows to use the connection as file-like object in
+        functions waiting for readiness, such as the ones defined in the
+        `selectors` module.
+        """
+        try:
+            return self.pgconn.socket
+        except pq.PQerror as exc:
+            raise e.OperationalError(str(exc))
+
     def cancel(self) -> None:
         """Cancel the current operation on the connection."""
         c = self.pgconn.get_cancel()
index 193896423c712e1ffce881ff0a40ca52c5754938..2f9347bf169f9e66eabd4a633af02078109a0a68 100644 (file)
@@ -7,6 +7,7 @@ import sys
 import time
 import queue
 import pytest
+import selectors
 import threading
 import subprocess as sp
 
@@ -170,3 +171,26 @@ def test_cancel(conn):
     # still working
     conn.rollback()
     assert cur.execute("select 1").fetchone()[0] == 1
+
+
+@pytest.mark.slow
+def test_identify_closure(conn, dsn):
+    conn2 = psycopg3.connect(dsn)
+
+    def closer():
+        time.sleep(0.3)
+        conn2.execute(
+            "select pg_terminate_backend(%s)", [conn.pgconn.backend_pid]
+        )
+
+    t0 = time.time()
+    sel = selectors.DefaultSelector()
+    sel.register(conn, selectors.EVENT_READ)
+    t = threading.Thread(target=closer)
+    t.start()
+
+    assert sel.select(timeout=1.0)
+    with pytest.raises(psycopg3.OperationalError):
+        conn.execute("select 1")
+    t1 = time.time()
+    assert 0.3 < t1 - t0 < 0.5
index 6b21db1d373d3bb651527d70aa9a07f22fd633c8..446db1fb9c61324ecddcb12d32bea9757945e5aa 100644 (file)
@@ -127,3 +127,26 @@ async def test_cancel(aconn):
     cur = aconn.cursor()
     await cur.execute("select 1")
     assert await cur.fetchone() == (1,)
+
+
+@pytest.mark.slow
+async def test_identify_closure(aconn, dsn):
+    conn2 = await psycopg3.AsyncConnection.connect(dsn)
+
+    async def closer():
+        await asyncio.sleep(0.3)
+        await conn2.execute(
+            "select pg_terminate_backend(%s)", [aconn.pgconn.backend_pid]
+        )
+
+    t0 = time.time()
+    ev = asyncio.Event()
+    loop = asyncio.get_event_loop()
+    loop.add_reader(aconn.fileno(), ev.set)
+    asyncio.ensure_future(closer())
+
+    await asyncio.wait_for(ev.wait(), 1.0)
+    with pytest.raises(psycopg3.OperationalError):
+        await aconn.execute("select 1")
+    t1 = time.time()
+    assert 0.3 < t1 - t0 < 0.5
index 8ab62ee77c3dd3c9097eb729ebe7f49e55fdd04e..83328f71a6c96601adf5c43e4b7f0da51739ab22 100644 (file)
@@ -511,3 +511,10 @@ def test_str(conn):
     assert "[IDLE]" in str(conn)
     conn.close()
     assert "[BAD]" in str(conn)
+
+
+def test_fileno(conn):
+    assert conn.fileno() == conn.pgconn.socket
+    conn.close()
+    with pytest.raises(psycopg3.OperationalError):
+        conn.fileno()
index 9ec28560e2e0af8827f473baa83a1b91066c2ad5..956cff151c0550517a767fa778189a80d1c14f7e 100644 (file)
@@ -529,3 +529,10 @@ async def test_str(aconn):
     assert "[IDLE]" in str(aconn)
     await aconn.close()
     assert "[BAD]" in str(aconn)
+
+
+async def test_fileno(aconn):
+    assert aconn.fileno() == aconn.pgconn.socket
+    await aconn.close()
+    with pytest.raises(psycopg3.OperationalError):
+        aconn.fileno()