Fixed async documentation after cursor.poll() dropped.

This commit is contained in:
Daniele Varrazzo 2010-04-19 23:49:14 +01:00
parent a57a9976b4
commit ac6938a26a
4 changed files with 86 additions and 76 deletions

View File

@ -6,10 +6,27 @@ More advanced topics
.. testsetup:: * .. testsetup:: *
import re import re
import select
cur.execute("CREATE TABLE atable (apoint point)") cur.execute("CREATE TABLE atable (apoint point)")
conn.commit() conn.commit()
def wait(conn):
while 1:
state = conn.poll()
if state == psycopg2.extensions.POLL_OK:
break
elif state == psycopg2.extensions.POLL_WRITE:
select.select([], [conn.fileno()], [])
elif state == psycopg2.extensions.POLL_READ:
select.select([conn.fileno()], [], [])
else:
raise psycopg2.OperationalError("poll() returned %s" % state)
aconn = psycopg2.connect(database='test', async=1)
wait(aconn)
acurs = aconn.cursor()
.. index:: .. index::
double: Subclassing; Cursor double: Subclassing; Cursor
double: Subclassing; Connection double: Subclassing; Connection
@ -255,47 +272,48 @@ Asynchronous support
.. versionadded:: 2.2.0 .. versionadded:: 2.2.0
Psycopg can issue asynchronous queries to a Postgresql database. An asynchronous Psycopg can issue asynchronous queries to a PostgreSQL database. An asynchronous
communication style is estabilished passing the parameter *async*\=1 to the communication style is estabilished passing the parameter *async*\=1 to the
`~psycopg2.connect()` function: the returned connection will work in `~psycopg2.connect()` function: the returned connection will work in
asynchronous mode. *asynchronous mode*.
In asynchronous mode, a Psycopg connection will rely on the caller to poll for In asynchronous mode, a Psycopg connection will rely on the caller to poll the
the socket file descriptor ready to accept data or a query result ready to be socket file descriptor, checking if it is ready to accept data or if a query
read from the server. The caller can use the method `~cursor.fileno()` to get result has been transferred and is ready to be read on the client. The caller
the connection file descriptor and `~cursor.poll()` to make communication can use the method `~connection.fileno()` to get the connection file
proceed. An application can use a loop like the one below to transfer data descriptor and `~connection.poll()` to make communication proceed according to
between the client and the server:: the current connection state.
def wait(conn_or_cur): The following is an example loop using methods `!fileno()` and `!poll()`
together with the Python |select()|_ function in order to carry on
asynchronous operations with Psycopg::
def wait(conn):
while 1: while 1:
state = conn_or_cur.poll() state = conn.poll()
if state == psycopg2.extensions.POLL_OK: if state == psycopg2.extensions.POLL_OK:
break break
elif state == psycopg2.extensions.POLL_WRITE: elif state == psycopg2.extensions.POLL_WRITE:
select.select([], [conn_or_cur.fileno()], []) select.select([], [conn.fileno()], [])
elif state == psycopg2.extensions.POLL_READ: elif state == psycopg2.extensions.POLL_READ:
select.select([conn_or_cur.fileno()], [], []) select.select([conn.fileno()], [], [])
else: else:
raise psycopg2.OperationalError("poll() returned %s" % state) raise psycopg2.OperationalError("poll() returned %s" % state)
After `!poll()` has returned `~psycopg2.extensions.POLL_OK`, the results are .. |select()| replace:: `!select()`
available in the cursor for regular reading:: .. _select(): http://docs.python.org/library/select.html#select.select
curs.execute("SELECT * FROM foo;") The above loop of course would block an entire application: in a real
wait(curs) asynchronous framework, `!select()` would be called on many file descriptors
for record in curs: waiting for any of them to be ready. Nonetheless the function can be used to
# use it... connect to a PostgreSQL server only using nonblocking commands and the
connection obtained can be used to perform further nonblocking queries. After
`!poll()` has returned `~psycopg2.extensions.POLL_OK`, and thus `!wait()` has
returned, the connection can be safely used:
The same loop should also be used to accomplish a connection with the server: >>> aconn = psycopg2.connect(database='test', async=1)
the connection is usable only after `connection.poll()` has returned `!POLL_OK`. >>> wait(aconn)
The `!connection` has a `~connection.fileno()` method too, so it is possible to >>> acurs = aconn.cursor()
use the same interface for the wait loop::
conn = psycopg2.connect(database='test', async=1)
wait(conn)
# Now you can have a cursor.
curs = conn.cursor()
Notice that there are a few other requirements to be met in order to have a Notice that there are a few other requirements to be met in order to have a
completely non-blocking connection attempt: see the libpq documentation for completely non-blocking connection attempt: see the libpq documentation for
@ -304,17 +322,29 @@ completely non-blocking connection attempt: see the libpq documentation for
.. |PQconnectStart| replace:: `!PQconnectStart()` .. |PQconnectStart| replace:: `!PQconnectStart()`
.. _PQconnectStart: http://www.postgresql.org/docs/8.4/static/libpq-connect.html#AEN33199 .. _PQconnectStart: http://www.postgresql.org/docs/8.4/static/libpq-connect.html#AEN33199
The same loop should be also used to perform nonblocking queries: after
sending a query via `~cursor.execute()` or `~cursor.callproc()`, call
`!poll()` on the connection available from `cursor.connection` until it
returns `!POLL_OK`, at which pont the query has been completely sent to the
server and, if it produced data, the results have been transferred to the
client and available using the regular cursor methods:
>>> acurs.execute("SELECT pg_sleep(5); SELECT 42;")
>>> wait(acurs.connection)
>>> acurs.fetchone()[0]
42
When an asynchronous query is being executed, `connection.executing()` returns When an asynchronous query is being executed, `connection.executing()` returns
`True`. Two cursors can't execute concurrent queries on the same asynchronous `True`. Two cursors can't execute concurrent queries on the same asynchronous
connection. connection.
There are several limitations in using asynchronous connections: the connection There are several limitations in using asynchronous connections: the
is always in :ref:`autocommit <autocommit>` mode and it is not possible to connection is always in :ref:`autocommit <autocommit>` mode and it is not
change it using `~connection.set_isolation_level()`. So transaction are not possible to change it using `~connection.set_isolation_level()`. So a
started at each query and is not possible to use methods `~connection.commit()` transaction is not implicitly started at the first query and is not possible
and `~connection.rollback()`: you can manually control transactions using to use methods `~connection.commit()` and `~connection.rollback()`: you can
`~cursor.execute()` to send commands :sql:`BEGIN`, :sql:`COMMIT` and manually control transactions using `~cursor.execute()` to send database
:sql:`ROLLBACK`. commands such as :sql:`BEGIN`, :sql:`COMMIT` and :sql:`ROLLBACK`.
With asynchronous connections it is also not possible to use With asynchronous connections it is also not possible to use
`~connection.set_client_encoding()`, `~cursor.executemany()`, :ref:`large `~connection.set_client_encoding()`, `~cursor.executemany()`, :ref:`large
@ -328,6 +358,7 @@ this will be probably implemented in a future release.
.. testcode:: .. testcode::
:hide: :hide:
aconn.close()
conn.rollback() conn.rollback()
cur.execute("DROP TABLE atable") cur.execute("DROP TABLE atable")
conn.commit() conn.commit()

View File

@ -329,19 +329,22 @@ The ``connection`` class
.. method:: poll() .. method:: poll()
Used during an asynchronous connection attempt, make communication Used during an asynchronous connection attempt, or when a cursor is
executing a query on an asynchronous connection, make communication
proceed if it wouldn't block. proceed if it wouldn't block.
Return one of the constants defined in :ref:`poll-constants`. If it Return one of the constants defined in :ref:`poll-constants`. If it
returns `~psycopg2.extensions.POLL_OK` the connection has been returns `~psycopg2.extensions.POLL_OK` then the connection has been
estabilished. Otherwise wait until the file descriptor is ready as estabilished or the query results are available on the client.
explained in :ref:`async-support`. Otherwise wait until the file descriptor returned by
`~connection.fileno()` is ready to read or to write, as explained in
:ref:`async-support`.
.. method:: fileno() .. method:: fileno()
Return the file descriptor associated with the connection to read the Return the file descriptor underlying the connection: useful to read
status during asynchronous communication. its status during asynchronous communication.
.. method:: executing() .. method:: executing()

View File

@ -390,34 +390,6 @@ The ``cursor`` class
.. _tzinfo: http://docs.python.org/library/datetime.html#tzinfo-objects .. _tzinfo: http://docs.python.org/library/datetime.html#tzinfo-objects
.. rubric:: Methods related to asynchronous support.
.. extension::
:ref:`Asynchronous support <async-support>` is a Psycopg extension to
the |DBAPI|.
.. method:: poll()
Used during asynchronous queries, make asynchronous communication
proceed if it wouldn't block.
Return `~psycopg2.extensions.POLL_OK` if the query has been fully
processed, `~psycopg2.extensions.POLL_READ` if the query has been sent
and the application should be waiting for the result to arrive or
`~psycopg2.extensions.POLL_WRITE` is the query is still being sent.
.. method:: fileno()
Return the file descriptor associated with the current connection to
make possible to use a cursor in a context where a file object would
be expected (like in a `select()` call).
.. rubric:: COPY-related methods .. rubric:: COPY-related methods
.. extension:: .. extension::

View File

@ -455,25 +455,29 @@ Poll constants
.. versionadded:: 2.2.0 .. versionadded:: 2.2.0
These values can be returned by `connection.poll()` and `cursor.poll()` during These values can be returned by `connection.poll()` during asynchronous
asynchronous communication. See :ref:`async-support`. connection and communication. See :ref:`async-support`.
.. data:: POLL_OK .. data:: POLL_OK
The data is available (or the file descriptor is ready for writing): there The data being read is available, or the file descriptor is ready for
is no need to block anymore. writing: reading or writing will not block.
.. data:: POLL_READ .. data:: POLL_READ
Upon receiving this value, the callback should wait for the connection Some data is being read from the backend, but it is not available yet on
file descriptor to be ready *for reading*. For example:: the client and reading would block. Upon receiving this value, the client
should wait for the connection file descriptor to be ready *for reading*.
For example::
select.select([conn.fileno()], [], []) select.select([conn.fileno()], [], [])
.. data:: POLL_WRITE .. data:: POLL_WRITE
Upon receiving this value, the callback should wait for the connection Some data is being sent to the backend but the connection file descriptor
file descriptor to be ready *for writing*. For example:: can't currently accept new data. Upon receiving this value, the client
should wait for the connection file descriptor to be ready *for writing*.
For example::
select.select([], [conn.fileno()], []) select.select([], [conn.fileno()], [])