--- /dev/null
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--- /dev/null
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+import sys
+from pathlib import Path
+
+from better import better_theme_path # type: ignore
+
+sys.path.append(str(Path(__file__).parent / "lib"))
+
+
+# -- Project information -----------------------------------------------------
+
+project = "psycopg3"
+copyright = "2020, Daniele Varrazzo and The Psycopg Team"
+author = "Daniele Varrazzo"
+release = "UNRELEASED"
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.intersphinx",
+ "sql_role",
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["_templates"]
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = "better"
+html_theme_path = [better_theme_path]
+html_show_sphinx = False
+html_theme_options = {
+ "linktotheme": False,
+ "cssfiles": ["_static/psycopg.css"],
+}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ["_static"]
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+default_role = "obj"
+
+intersphinx_mapping = {"py": ("https://docs.python.org/3", None)}
--- /dev/null
+The ``Connection`` classes
+==========================
+
+The `Connection` and `AsyncConnection` classes are the main wrappers for a
+PostgreSQL database session. You can imagine them similar to a :program:`psql`
+session.
+
+One of the differences compared to :program:`psql` is that a `Connection`
+usually handles a transaction automatically: other sessions will not be able
+to see the changes until you have committed them, more or less explicitly.
+Take a look to :ref:`transactions` for the details.
+
+.. autoclass:: psycopg3.Connection
+
+ This class implements a DBAPI-compliant interface. It is what you want to
+ use if you write a "classic", blocking program (eventually using threads or
+ Eventlet/gevent for concurrency. If your program uses `asyncio` you might
+ want to use `AsyncConnection` instead.
+
+ Connections behave as context managers: on block exit, the current
+ transaction will be committed (or rolled back, in case of exception) and
+ the connection will be closed.
+
+ .. automethod:: connect
+
+ Connection parameters can be passed either as a `conninfo string`__ (a
+ ``postgresql://`` url or a list of ``key=value pairs``) or as keywords.
+ Keyword parameters override the ones specified in the connection string.
+
+ .. __: https://www.postgresql.org/docs/current/libpq-connect.html
+ #LIBPQ-CONNSTRING
+
+ This method is also aliased as `psycopg3.connect()`.
+
+ .. seealso::
+
+ - the list of `the accepted connection parameters`__
+ - the `environment varialbes`__ affecting connection
+
+ .. __: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
+ .. __: https://www.postgresql.org/docs/current/libpq-envars.html
+
+ .. rubric:: Methods you will need every day
+
+ .. automethod:: cursor
+ .. automethod:: commit
+ .. automethod:: rollback
+ .. automethod:: close
+ .. autoproperty:: closed
+
+ .. rubric:: Methods you will need if you do something cool
+
+ .. automethod:: notifies
+
+ Notifies are recevied after using :sql:`LISTEN` in a connection, when
+ any sessions in the database generates a :sql:`NOTIFY` on one of the
+ listened channels.
+
+ .. automethod:: add_notify_handler
+ .. automethod:: remove_notify_handler
+
+ See :ref:`async-notify` for details.
+
+.. autoclass:: psycopg3.AsyncConnection
+
+ This class implements a DBAPI-inspired interface, with all the blocking
+ methods implemented as coroutines. Unless specified otherwise,
+ non-blocking methods are shared with the `Connection` class.
+
+ The following methods have the same behaviour of the matching `~Connection`
+ methods, but have an `async` interface.
+
+ .. automethod:: connect
+ .. automethod:: close
+ .. automethod:: cursor
+ .. automethod:: commit
+ .. automethod:: rollback
+ .. automethod:: notifies
+
+
+.. autoclass:: psycopg3.Notify
--- /dev/null
+.. index::
+ pair: psycopg2; Differences
+
+Differences from psycopg2
+=========================
+
+`!psycopg3` uses the common DBAPI structure of many other database adapter and
+tries to behave as close as possible to `!psycopg2`. There are however a few
+differences to be aware of.
+
+
+Server-side binding
+-------------------
+
+`!psycopg3` sends the query and the parameters to the server separately,
+instead of merging them client-side. PostgreSQL may behave slightly
+differently in this case, usually throwing an error and suggesting to use an
+explicit cast.
+
+.. code:: python
+
+ cur.execute("select '[10,20,30]'::jsonb -> 1").fetchone()
+ # returns (20,)
+
+ cur.execute("select '[10,20,30]'::jsonb -> %s", [1]).fetchone()
+ # raises an exception:
+ # UndefinedFunction: operator does not exist: jsonb -> numeric
+
+ cur.execute("select '[10,20,30]'::jsonb -> %s::int", [1]).fetchone()
+ # returns (20,)
+
+PostgreSQL will also reject the execution of several queries at once
+(separated by semicolon), if they contain parameters. If parameters are used
+you should use distinct `execute()` calls; otherwise you may consider merging
+the query client-side, using `psycopg3.sql` module.
+
+
+Different adaptation system
+---------------------------
+
+The adaptation system has been completely rewritten, in order to address
+server-side parameters adaptation, but also to consider performance,
+flexibility, ease of customization.
+
+Builtin data types should work as expected; if you have wrapped a custom data
+type you should check the `<ref> Adaptation` topic.
+
+
+Other differences
+-----------------
+
+When the connection is used as context manager, at the end of the context the
+connection will be closed. In psycopg2 only the transaction is closed, so a
+connection can be used in several contexts, but the behaviour is surprising
+for people used to several other Python classes wrapping resources, such as
+files.
+
+
+What's new in psycopg3
+======================
+
+- `asyncio` support.
+- Several data types are adapted out-of-the-box: uuid, network, range, bytea,
+ array of any supported type are dealt with automatically.
+- Access to the low-level libpq functions.
--- /dev/null
+===================================================
+Psycopg 3 -- PostgreSQL database adapter for Python
+===================================================
+
+`!psycopg3` is a newly designed PostgreSQL_ database adapter for the Python_
+programming language.
+
+`!psycopg3` presents a familiar interface for everyone who has used
+`!psycopg2` or any other `DB API 2.0`__ database adapter, but allows to use
+more modern PostgreSQL and Python features, such as `asyncio` support,
+server-side parameters binding, binary communication, a better integration of
+the COPY support.
+
+.. _Python: https://www.python.org/
+.. _PostgreSQL: https://www.postgresql.org/
+.. __: https://www.python.org/dev/peps/pep-0249/
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ install
+ usage
+ from_pg2
+ connection
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
--- /dev/null
+.. _installation:
+
+Installation
+============
+
+`!psycopg3` is still in a development phase, and hasn't been released yet.
+
+Please refer to `the README`__ for the current installation state, and please know
+that things may change.
+
+.. __: https://github.com/psycopg/psycopg3#readme
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+ sql role
+ ~~~~~~~~
+
+ An interpreted text role to style SQL syntax in Psycopg documentation.
+
+ :copyright: Copyright 2010-2020 by Daniele Varrazzo.
+"""
+
+from docutils import nodes, utils
+from docutils.parsers.rst import roles
+
+
+def sql_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ options["classes"] = ["sql"]
+ return [nodes.literal(rawtext, text, **options)], []
+
+
+def setup(app):
+ roles.register_local_role("sql", sql_role)
--- /dev/null
+Sphinx >= 3.3, < 3.4
+docutils >= 0.16, < 0.17
+sphinx-better-theme >= 0.1.5, < 0.2
--- /dev/null
+.. index::
+ pair: Example; Usage
+
+.. _usage:
+
+Basic module usage
+==================
+
+The basic Psycopg usage is common to all the database adapters implementing
+the `DB API`__ protocol. Here is an interactive session showing some of the
+basic commands:
+
+.. __: https://www.python.org/dev/peps/pep-0249/
+
+.. code:: python
+
+ import psycopg3
+
+ # Connect to an existing database
+ conn = psycopg3.connect("dbname=test user=postgres")
+
+ # Open a cursor to perform database operations
+ cur = conn.cursor()
+
+ # Execute a command: this creates a new table
+ cur.execute("""
+ CREATE TABLE test (
+ id serial PRIMARY KEY,
+ num integer,
+ data text)
+ """)
+
+ # Pass data to fill a query placeholders and let Psycopg perform
+ # the correct conversion (no SQL injections!)
+ cur.execute(
+ "INSERT INTO test (num, data) VALUES (%s, %s)",
+ (100, "abc'def"))
+
+ # Query the database and obtain data as Python objects.
+ cur.execute("SELECT * FROM test")
+ cur.fetchone()
+ # will return (1, 100, "abc'def")
+
+ # You can use `cur.fetchmany()`, `cur.fetchall()` to return a list
+ # of several records, or even iterate on the cursor
+ for record in cur:
+ print(record)
+
+ # Make the changes to the database persistent
+ conn.commit()
+
+ # Close communication with the database
+ cur.close()
+ conn.close()
+
+
+Note that the `cursor.execute()` method returns the cursor itself, so the
+`fetch*()` methods can be appended right after it.
+
+.. code:: python
+
+ cur.execute("SELECT * FROM test").fetchone()
+
+ for record in cur.execute("SELECT * FROM test"):
+ print(record)
+
+
+The connections and cursors act as context managers, so you can run:
+
+.. code:: python
+
+ with psycopg3.connect("dbname=test user=postgres") as conn:
+ with conn.cursor() as cur:
+ cur.execute(
+ "INSERT INTO test (num, data) VALUES (%s, %s)",
+ (100, "abc'def"))
+ cur.execute("SELECT * FROM test").fetchone()
+ # will return (1, 100, "abc'def")
+
+ # the cursor is closed upon leaving the context
+
+ # the transaction is committed on successful exit of the context
+ # and the connection closed
+
+
+If you are working in an `asyncio` project you can use a very similar pattern:
+
+.. code:: python
+
+ async with await psycopg3.AsyncConnection.connect(
+ "dbname=test user=postgres") as aconn:
+ async with await aconn.cursor() as acur:
+ await acur.execute(
+ "INSERT INTO test (num, data) VALUES (%s, %s)",
+ (100, "abc'def"))
+ await acur.execute("SELECT * FROM test")
+ await acur.fetchone()
+ # will return (1, 100, "abc'def")
+
+
+The main entry points of Psycopg are:
+
+- The function `~psycopg3.connect()` creates a new database session and
+ returns a new `connection` instance. `psycopg3.AsyncConnection.connect()`
+ creates an asyncio connection instead.
+
+- The `connection` class encapsulates a database session. It allows to:
+
+ - create new `cursor` instances using the `~connection.cursor()` method to
+ execute database commands and queries,
+
+ - terminate transactions using the methods `~connection.commit()` or
+ `~connection.rollback()`.
+
+- The class `cursor` allows interaction with the database:
+
+ - send commands to the database using methods such as `~cursor.execute()`
+ and `~cursor.executemany()`,
+
+ - retrieve data from the database :ref:`by iteration <cursor-iterable>` or
+ using methods such as `~cursor.fetchone()`, `~cursor.fetchmany()`,
+ `~cursor.fetchall()`.
+
+
+.. index::
+ pair: Query; Parameters
+
+.. _query-parameters:
+
+Passing parameters to SQL queries
+---------------------------------
+
+TODO: lift from psycopg2 docs
+
+
+.. _transactions:
+
+Transaction management
+----------------------
+
+TODO:
+
+
+.. index::
+ pair: Asynchronous; Notifications
+ pair: LISTEN; SQL command
+ pair: NOTIFY; SQL command
+
+.. _async-notify:
+
+Asynchronous notifications
+--------------------------
+
+Psycopg allows asynchronous interaction with other database sessions using the
+facilities offered by PostgreSQL commands |LISTEN|_ and |NOTIFY|_. Please
+refer to the PostgreSQL documentation for examples about how to use this form
+of communication.
+
+.. |LISTEN| replace:: :sql:`LISTEN`
+.. _LISTEN: https://www.postgresql.org/docs/current/static/sql-listen.html
+.. |NOTIFY| replace:: :sql:`NOTIFY`
+.. _NOTIFY: https://www.postgresql.org/docs/current/static/sql-notify.html
+
+Because of the way sessions interact with notifications (see |NOTIFY|_
+documentation), you should keep the connection in `~connection.autocommit`
+mode if you wish to receive or send notifications in a timely manner.
+
+Notifications are received as instances of `~psycopg3.Notify`. If you are
+reserving a connection only to receive notifications, the simplest way is to
+consume the `~psycopg3.Connection.notifies` generator. The generator can be
+stopped using ``close()``. The following example will print notifications and
+stop when one containing the ``stop`` message is received.
+
+.. code:: python
+
+ import psycopg3
+ conn = psycopg3.connect("", autocommit=True)
+ conn.cursor().execute("LISTEN mychan")
+ gen = conn.notifies()
+ for notify in gen:
+ print(notify)
+ if notify.payload == "stop":
+ gen.close()
+ print("there, I stopped")
+
+If you run some :sql:`NOTIFY` in a :program:`psql` session:
+
+.. code:: psql
+
+ =# notify mychan, 'hello';
+ NOTIFY
+ =# notify mychan, 'hey';
+ NOTIFY
+ =# notify mychan, 'stop';
+ NOTIFY
+
+You may get output from the Python process such as::
+
+ Notify(channel='mychan', payload='hello', pid=961823)
+ Notify(channel='mychan', payload='hey', pid=961823)
+ Notify(channel='mychan', payload='stop', pid=961823)
+ there, I stopped
+
+Alternatively, you can use `~psycopg3.Connection.add_notify_handler()` to
+register a callback function, which will be invoked whenever a notification is
+received, during the normal query processing; you will be then able to use the
+connection normally. Please note that in this case notifications will not be
+received immediately, but only during a connection operation, such as a query.
+
+.. code:: python
+
+ conn.add_notify_handler(lambda n: print(f"got this: {n}"))
+
+ # meanwhile in psql...
+ # =# notify mychan, 'hey';
+ # NOTIFY
+
+ print(conn.cursor().execute("select 1").fetchone())
+ # got this: Notify(channel='mychan', payload='hey', pid=961823)
+ # (1,)
class Notify(NamedTuple):
+ """An asynchronous notification received from the database."""
+
channel: str
payload: str
pid: int
@property
def closed(self) -> bool:
+ """`true` if the connection is closed."""
return self.status == self.ConnStatus.BAD
@property
)
def add_notify_handler(self, callback: NotifyHandler) -> None:
+ """
+ Register a callable to be invoked whenever a notification is received.
+ """
self._notify_handlers.append(callback)
def remove_notify_handler(self, callback: NotifyHandler) -> None:
+ """
+ Unregister a notification callable previously registered.
+ """
self._notify_handlers.remove(callback)
@staticmethod
class Connection(BaseConnection):
"""
- Wrap a connection to the database.
-
- This class implements a DBAPI-compliant interface.
+ Wrapper for a connection to the database.
"""
cursor_factory: Type[cursor.Cursor]
self.close()
def close(self) -> None:
+ """Close the database connection."""
self.pgconn.finish()
def cursor(
self, name: str = "", format: pq.Format = pq.Format.TEXT
) -> cursor.Cursor:
+ """Return a new cursor to send commands and query the connection."""
cur = self._cursor(name, format=format)
return cast(cursor.Cursor, cur)
)
def commit(self) -> None:
+ """Commit any pending transaction to the database."""
with self.lock:
self._exec_commit_rollback(b"commit")
def rollback(self) -> None:
+ """Roll back to the start of any pending transaction."""
with self.lock:
self._exec_commit_rollback(b"rollback")
class AsyncConnection(BaseConnection):
"""
- Wrap an asynchronous connection to the database.
-
- This class implements a DBAPI-inspired interface, with all the blocking
- methods implemented as coroutines.
+ Asynchronous wrapper for a connection to the database.
"""
cursor_factory: Type[cursor.AsyncCursor]
async def connect(
cls, conninfo: str = "", *, autocommit: bool = False, **kwargs: Any
) -> "AsyncConnection":
- """`asyncio` version of `~Connection.connect()`."""
conninfo = make_conninfo(conninfo, **kwargs)
gen = connect(conninfo)
pgconn = await cls.wait(gen)