mirror of
https://github.com/psycopg/psycopg2.git
synced 2025-01-31 09:24:07 +03:00
The documentation is now mostly doctest-able
This commit is contained in:
parent
67de65040c
commit
323340abc6
104
doc/advanced.rst
104
doc/advanced.rst
|
@ -10,6 +10,13 @@ More advanced topics
|
|||
.. _subclassing-connection:
|
||||
.. _subclassing-cursor:
|
||||
|
||||
.. testsetup:: *
|
||||
|
||||
import re
|
||||
|
||||
cur.execute("CREATE TABLE atable (apoint point)")
|
||||
conn.commit()
|
||||
|
||||
Connection and cursor factories
|
||||
-------------------------------
|
||||
|
||||
|
@ -42,8 +49,8 @@ An example of cursor subclass performing logging is::
|
|||
raise
|
||||
|
||||
conn = psycopg2.connect(DSN)
|
||||
curs = conn.cursor(cursor_factory=LoggingCursor)
|
||||
curs.execute("INSERT INTO mytable VALUES (%s, %s, %s);",
|
||||
cur = conn.cursor(cursor_factory=LoggingCursor)
|
||||
cur.execute("INSERT INTO mytable VALUES (%s, %s, %s);",
|
||||
(10, 20, 30))
|
||||
|
||||
|
||||
|
@ -78,22 +85,24 @@ conversion of the wrapped object.
|
|||
single: Example; Types adaptation
|
||||
|
||||
Example: mapping of a :class:`!Point` class into the |point|_ PostgreSQL
|
||||
geometric type::
|
||||
geometric type:
|
||||
|
||||
from psycopg2.extensions import adapt, register_adapter, AsIs
|
||||
.. doctest::
|
||||
|
||||
class Point(object):
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
>>> from psycopg2.extensions import adapt, register_adapter, AsIs
|
||||
|
||||
def adapt_point(point):
|
||||
return AsIs("'(%s, %s)'" % (adapt(point.x), adapt(point.y)))
|
||||
>>> class Point(object):
|
||||
... def __init__(self, x, y):
|
||||
... self.x = x
|
||||
... self.y = y
|
||||
|
||||
register_adapter(Point, adapt_point)
|
||||
>>> def adapt_point(point):
|
||||
... return AsIs("'(%s, %s)'" % (adapt(point.x), adapt(point.y)))
|
||||
|
||||
curs.execute("INSERT INTO atable (apoint) VALUES (%s)",
|
||||
(Point(1.23, 4.56),))
|
||||
>>> register_adapter(Point, adapt_point)
|
||||
|
||||
>>> cur.execute("INSERT INTO atable (apoint) VALUES (%s)",
|
||||
... (Point(1.23, 4.56),))
|
||||
|
||||
|
||||
.. |point| replace:: :sql:`point`
|
||||
|
@ -117,55 +126,54 @@ through an user-defined adapting function. An adapter function takes two
|
|||
arguments: the object string representation as returned by PostgreSQL and the
|
||||
cursor currently being read, and should return a new Python object. For
|
||||
example, the following function parses the PostgreSQL :sql:`point`
|
||||
representation into the previously defined :class:`!Point` class::
|
||||
representation into the previously defined :class:`!Point` class:
|
||||
|
||||
def cast_point(value, curs):
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
# Convert from (f1, f2) syntax using a regular expression.
|
||||
m = re.match(r"\(([^)]+),([^)]+)\)", value)
|
||||
if m:
|
||||
return Point(float(m.group(1)), float(m.group(2)))
|
||||
else:
|
||||
raise InterfaceError("bad point representation: %r" % value)
|
||||
>>> def cast_point(value, cur):
|
||||
... if value is None:
|
||||
... return None
|
||||
...
|
||||
... # Convert from (f1, f2) syntax using a regular expression.
|
||||
... m = re.match(r"\(([^)]+),([^)]+)\)", value)
|
||||
... if m:
|
||||
... return Point(float(m.group(1)), float(m.group(2)))
|
||||
... else:
|
||||
... raise InterfaceError("bad point representation: %r" % value)
|
||||
|
||||
|
||||
In order to create a mapping from a PostgreSQL type (either standard or
|
||||
user-defined), its OID must be known. It can be retrieved either by the second
|
||||
column of the :attr:`cursor.description`::
|
||||
column of the :attr:`cursor.description`:
|
||||
|
||||
curs.execute("SELECT NULL::point")
|
||||
point_oid = curs.description[0][1] # usually returns 600
|
||||
>>> cur.execute("SELECT NULL::point")
|
||||
>>> point_oid = cur.description[0][1] # usually returns 600
|
||||
|
||||
or by querying the system catalogs for the type name and namespace (the
|
||||
namespace for system objects is :sql:`pg_catalog`)::
|
||||
namespace for system objects is :sql:`pg_catalog`):
|
||||
|
||||
curs.execute("""
|
||||
SELECT pg_type.oid
|
||||
FROM pg_type JOIN pg_namespace
|
||||
ON typnamespace = pg_namespace.oid
|
||||
WHERE typname = %(typename)s
|
||||
AND nspname = %(namespace)s""",
|
||||
{'typename': 'point', 'namespace': 'pg_catalog'})
|
||||
>>> cur.execute("""
|
||||
... SELECT pg_type.oid
|
||||
... FROM pg_type JOIN pg_namespace
|
||||
... ON typnamespace = pg_namespace.oid
|
||||
... WHERE typname = %(typename)s
|
||||
... AND nspname = %(namespace)s""",
|
||||
... {'typename': 'point', 'namespace': 'pg_catalog'})
|
||||
>>> point_oid = cur.fetchone()[0]
|
||||
|
||||
point_oid = curs.fetchone()[0]
|
||||
After you know the object OID, you must can and register the new type:
|
||||
|
||||
After you know the object OID, you must can and register the new type::
|
||||
|
||||
POINT = psycopg2.extensions.new_type((point_oid,), "POINT", cast_point)
|
||||
psycopg2.extensions.register_type(POINT)
|
||||
>>> POINT = psycopg2.extensions.new_type((point_oid,), "POINT", cast_point)
|
||||
>>> psycopg2.extensions.register_type(POINT)
|
||||
|
||||
The :func:`~psycopg2.extensions.new_type` function binds the object OIDs
|
||||
(more than one can be specified) to the adapter function.
|
||||
:func:`~psycopg2.extensions.register_type` completes the spell. Conversion
|
||||
is automatically performed when a column whose type is a registered OID is
|
||||
read::
|
||||
read:
|
||||
|
||||
>>> curs.execute("SELECT '(10.2,20.3)'::point")
|
||||
>>> point = curs.fetchone()[0]
|
||||
>>> cur.execute("SELECT '(10.2,20.3)'::point")
|
||||
>>> point = cur.fetchone()[0]
|
||||
>>> print type(point), point.x, point.y
|
||||
<class '__main__.Point'> 10.2 20.3
|
||||
<class 'Point'> 10.2 20.3
|
||||
|
||||
|
||||
|
||||
|
@ -318,3 +326,11 @@ call::
|
|||
print row
|
||||
|
||||
|
||||
.. testcode::
|
||||
:hide:
|
||||
|
||||
conn.rollback()
|
||||
cur.execute("DROP TABLE atable")
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
|
31
doc/conf.py
31
doc/conf.py
|
@ -22,7 +22,8 @@ sys.path.append(os.path.abspath('.'))
|
|||
|
||||
# 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.todo', 'sphinx.ext.ifconfig' ]
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.ifconfig',
|
||||
'sphinx.ext.doctest']
|
||||
|
||||
# Specific extensions for Psycopg documentation.
|
||||
extensions += [ 'dbapi_extension', 'sql_role' ]
|
||||
|
@ -222,3 +223,31 @@ latex_documents = [
|
|||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
|
||||
|
||||
doctest_global_setup = """
|
||||
|
||||
import os
|
||||
import psycopg2
|
||||
|
||||
def test_connect():
|
||||
try:
|
||||
dsn = os.environ['PSYCOPG2_DSN']
|
||||
except KeyError:
|
||||
assert False, "You need to set the environment variable PSYCOPG2_DSN" \
|
||||
" in order to test the documentation!"
|
||||
return psycopg2.connect(dsn)
|
||||
|
||||
conn = test_connect()
|
||||
cur = conn.cursor()
|
||||
|
||||
def create_test_table():
|
||||
try:
|
||||
cur.execute("CREATE TABLE test (id SERIAL PRIMARY KEY, num INT, data TEXT)")
|
||||
except:
|
||||
conn.rollback()
|
||||
cur.execute("DELETE FROM test")
|
||||
cur.execute("SELECT setval('test_id_seq', 1, False)")
|
||||
conn.commit()
|
||||
|
||||
"""
|
||||
|
|
|
@ -3,6 +3,11 @@ The ``connection`` class
|
|||
|
||||
.. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from pprint import pprint
|
||||
import psycopg2.extensions
|
||||
|
||||
.. class:: connection
|
||||
|
||||
Handles the connection to a PostgreSQL database instance. It encapsulates
|
||||
|
@ -155,12 +160,16 @@ The ``connection`` class
|
|||
.. attribute:: notices
|
||||
|
||||
A list containing all the database messages sent to the client during
|
||||
the session. ::
|
||||
the session.
|
||||
|
||||
.. doctest::
|
||||
:options: NORMALIZE_WHITESPACE
|
||||
|
||||
>>> cur.execute("CREATE TABLE foo (id serial PRIMARY KEY);")
|
||||
>>> conn.notices
|
||||
>>> pprint(conn.notices)
|
||||
['NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"\n',
|
||||
'NOTICE: CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"\n']
|
||||
>>> cur.execute("DROP TABLE foo;")
|
||||
|
||||
To avoid a leak in case excessive notices are generated, only the last
|
||||
50 messages are kept.
|
||||
|
|
|
@ -3,6 +3,19 @@ The ``cursor`` class
|
|||
|
||||
.. sectionauthor:: Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||
|
||||
.. testsetup:: *
|
||||
|
||||
from StringIO import StringIO
|
||||
import sys
|
||||
|
||||
create_test_table()
|
||||
|
||||
# initial data
|
||||
cur.executemany("INSERT INTO test (num, data) VALUES (%s, %s)",
|
||||
[(100, "abc'def"), (None, 'dada'), (42, 'bar')])
|
||||
conn.commit()
|
||||
|
||||
|
||||
.. class:: cursor
|
||||
|
||||
Allows Python code to execute PostgreSQL command in a database session.
|
||||
|
@ -123,7 +136,7 @@ The ``cursor`` class
|
|||
|
||||
Return a query string after arguments binding. The string returned is
|
||||
exactly the one that would be sent to the database running the
|
||||
:meth:`~cursor.execute` method or similar. ::
|
||||
:meth:`~cursor.execute` method or similar.
|
||||
|
||||
>>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
|
||||
"INSERT INTO test (num, data) VALUES (42, E'bar')"
|
||||
|
@ -189,7 +202,7 @@ The ``cursor`` class
|
|||
|
||||
:class:`cursor` objects are iterable, so, instead of calling
|
||||
explicitly :meth:`~cursor.fetchone` in a loop, the object itself can
|
||||
be used::
|
||||
be used:
|
||||
|
||||
>>> cur.execute("SELECT * FROM test;")
|
||||
>>> for record in cur:
|
||||
|
@ -197,17 +210,17 @@ The ``cursor`` class
|
|||
...
|
||||
(1, 100, "abc'def")
|
||||
(2, None, 'dada')
|
||||
(4, 42, 'bar')
|
||||
(3, 42, 'bar')
|
||||
|
||||
|
||||
.. method:: fetchone()
|
||||
|
||||
Fetch the next row of a query result set, returning a single tuple,
|
||||
or ``None`` when no more data is available::
|
||||
or ``None`` when no more data is available:
|
||||
|
||||
>>> cur.execute("SELECT * FROM test WHERE id = %s", (4,))
|
||||
>>> cur.execute("SELECT * FROM test WHERE id = %s", (3,))
|
||||
>>> cur.fetchone()
|
||||
(4, 42, 'bar')
|
||||
(3, 42, 'bar')
|
||||
|
||||
A :exc:`~psycopg2.ProgrammingError` is raised if the previous call
|
||||
to |execute*|_ did not produce any result set or no call was issued
|
||||
|
@ -224,13 +237,13 @@ The ``cursor`` class
|
|||
the number of rows to be fetched. The method should try to fetch as
|
||||
many rows as indicated by the size parameter. If this is not possible
|
||||
due to the specified number of rows not being available, fewer rows
|
||||
may be returned::
|
||||
may be returned:
|
||||
|
||||
>>> cur.execute("SELECT * FROM test;")
|
||||
>>> cur.fetchmany(2)
|
||||
[(1, 100, "abc'def"), (2, None, 'dada')]
|
||||
>>> cur.fetchmany(2)
|
||||
[(4, 42, 'bar')]
|
||||
[(3, 42, 'bar')]
|
||||
>>> cur.fetchmany(2)
|
||||
[]
|
||||
|
||||
|
@ -252,7 +265,7 @@ The ``cursor`` class
|
|||
|
||||
>>> cur.execute("SELECT * FROM test;")
|
||||
>>> cur.fetchall()
|
||||
[(1, 100, "abc'def"), (2, None, 'dada'), (4, 42, 'bar')]
|
||||
[(1, 100, "abc'def"), (2, None, 'dada'), (3, 42, 'bar')]
|
||||
|
||||
A :exc:`~psycopg2.ProgrammingError` is raised if the previous call to
|
||||
|execute*|_ did not produce any result set or no call was issued yet.
|
||||
|
@ -278,7 +291,7 @@ The ``cursor`` class
|
|||
|
||||
According to the |DBAPI|_, the exception raised for a cursor out
|
||||
of bound should have been :exc:`!IndexError`. The best option is
|
||||
probably to catch both exceptions in your code::
|
||||
probably to catch both exceptions in your code:
|
||||
|
||||
try:
|
||||
cur.scroll(1000 * 1000)
|
||||
|
@ -359,7 +372,7 @@ The ``cursor`` class
|
|||
|
||||
Read-only attribute containing the body of the last query sent to the
|
||||
backend (including bound arguments). ``None`` if no query has been
|
||||
executed yet::
|
||||
executed yet:
|
||||
|
||||
>>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
|
||||
>>> cur.query
|
||||
|
@ -373,7 +386,7 @@ The ``cursor`` class
|
|||
.. attribute:: statusmessage
|
||||
|
||||
Read-only attribute containing the message returned by the last
|
||||
command::
|
||||
command:
|
||||
|
||||
>>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
|
||||
>>> cur.statusmessage
|
||||
|
@ -429,14 +442,13 @@ The ``cursor`` class
|
|||
The :obj:`!columns` argument is a sequence containing the name of the
|
||||
fields where the read data will be entered. Its length and column
|
||||
type should match the content of the read file. If not specifies, it
|
||||
is assumed that the entire table matches the file structure. ::
|
||||
is assumed that the entire table matches the file structure.
|
||||
|
||||
>>> f = StringIO("42\tfoo\n74\tbar\n")
|
||||
>>> cur.copy_from(f, 'test', columns=('num', 'data'))
|
||||
|
||||
>>> cur.execute("select * from test where id > 5;")
|
||||
>>> cur.fetchall()
|
||||
[(7, 42, 'foo'), (8, 74, 'bar')]
|
||||
[(6, 42, 'foo'), (7, 74, 'bar')]
|
||||
|
||||
.. versionchanged:: 2.0.6
|
||||
added the :obj:`columns` parameter.
|
||||
|
@ -452,11 +464,12 @@ The ``cursor`` class
|
|||
:obj:`!null` represents :sql:`NULL` values in the file.
|
||||
|
||||
The :obj:`!columns` argument is a sequence of field names: if not
|
||||
``None`` only the specified fields will be included in the dump. ::
|
||||
``None`` only the specified fields will be included in the dump.
|
||||
|
||||
>>> cur.copy_to(sys.stdout, 'test', sep="|")
|
||||
1|100|abc'def
|
||||
2|\N|dada
|
||||
...
|
||||
|
||||
.. versionchanged:: 2.0.6
|
||||
added the :obj:`columns` parameter.
|
||||
|
@ -472,12 +485,13 @@ The ``cursor`` class
|
|||
open, writeable file for :sql:`COPY TO`. The optional :obj:`!size`
|
||||
argument, when specified for a :sql:`COPY FROM` statement, will be
|
||||
passed to :obj:`!file`\ 's read method to control the read buffer
|
||||
size. ::
|
||||
size.
|
||||
|
||||
>>> cur.copy_expert("COPY test TO STDOUT WITH CSV HEADER", sys.stdout)
|
||||
id,num,data
|
||||
1,100,abc'def
|
||||
2,,dada
|
||||
...
|
||||
|
||||
.. |COPY| replace:: :sql:`COPY`
|
||||
.. __: http://www.postgresql.org/docs/8.4/static/sql-copy.html
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
|
||||
.. module:: psycopg2.extensions
|
||||
|
||||
.. testsetup:: *
|
||||
|
||||
from psycopg2.extensions import AsIs, QuotedString, ISOLATION_LEVEL_AUTOCOMMIT
|
||||
from psycopg2._psycopg import Binary
|
||||
|
||||
The module contains a few objects and function extending the minimum set of
|
||||
functionalities defined by the |DBAPI|_.
|
||||
|
||||
|
@ -94,7 +99,7 @@ deal with Python objects adaptation:
|
|||
|
||||
.. method:: getquoted()
|
||||
|
||||
Return the :meth:`str` conversion of the wrapped object. ::
|
||||
Return the :meth:`str` conversion of the wrapped object.
|
||||
|
||||
>>> AsIs(42).getquoted()
|
||||
'42'
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
|
||||
.. module:: psycopg2.extras
|
||||
|
||||
.. testsetup::
|
||||
|
||||
import psycopg2.extras
|
||||
|
||||
create_test_table()
|
||||
|
||||
This module is a generic place used to hold little helper functions and
|
||||
classes until a better place in the distribution is found.
|
||||
|
||||
|
@ -22,11 +28,13 @@ similar to the Python dictionaries instead of the tuples. You can use it
|
|||
either passing :class:`DictConnection` as :obj:`!connection_factory` argument
|
||||
to the :func:`~psycopg2.connect` function or passing :class:`DictCursor` as
|
||||
the :class:`!cursor_factory` argument to the :meth:`~connection.cursor` method
|
||||
of a regular :class:`connection`. ::
|
||||
of a regular :class:`connection`.
|
||||
|
||||
>>> conn = psycopg2.connect(database='test')
|
||||
>>> cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||
>>> cur.execute("SELECT * FROM test")
|
||||
>>> dict_cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||
>>> dict_cur.execute("INSERT INTO test (num, data) VALUES(%s, %s)",
|
||||
... (100, "abc'def"))
|
||||
>>> dict_cur.execute("SELECT * FROM test")
|
||||
>>> rec = dict_cur.fetchone()
|
||||
>>> rec['id']
|
||||
1
|
||||
>>> rec['num']
|
||||
|
@ -85,17 +93,15 @@ UUID data type
|
|||
.. versionadded:: 2.0.9
|
||||
.. versionchanged:: 2.0.13 added UUID array support.
|
||||
|
||||
::
|
||||
|
||||
>>> psycopg2.extras.register_uuid()
|
||||
|
||||
# Python UUID can be used in SQL queries
|
||||
<psycopg2._psycopg.type object at 0x...>
|
||||
>>>
|
||||
>>> # Python UUID can be used in SQL queries
|
||||
>>> import uuid
|
||||
>>> psycopg2.extensions.adapt(uuid.uuid4()).getquoted()
|
||||
"'1ad2b180-c17e-46ad-ad94-e1f0dfc7c34b'::uuid"
|
||||
|
||||
# PostgreSQL UUID are transformed into Python UUID objects.
|
||||
>>> conn = psycopg2.connect(database='test')
|
||||
>>> cur = conn.cursor()
|
||||
"'...-...-...-...-...'::uuid"
|
||||
>>>
|
||||
>>> # PostgreSQL UUID are transformed into Python UUID objects.
|
||||
>>> cur.execute("SELECT 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid")
|
||||
>>> cur.fetchone()[0]
|
||||
UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')
|
||||
|
@ -112,12 +118,9 @@ UUID data type
|
|||
|
||||
INET data type
|
||||
--------------
|
||||
::
|
||||
|
||||
>>> psycopg2.extras.register_inet()
|
||||
|
||||
>>> conn = psycopg2.connect(database='test')
|
||||
>>> cur = conn.cursor()
|
||||
<psycopg2._psycopg.type object at 0x...>
|
||||
>>> cur.execute("SELECT '192.168.0.1'::inet")
|
||||
>>> cur.fetchone()[0].addr
|
||||
'192.168.0.1'
|
||||
|
|
|
@ -107,15 +107,18 @@ available through the following exceptions:
|
|||
.. extension::
|
||||
|
||||
The :attr:`~Error.pgerror` and :attr:`~Error.pgcode` attributes are
|
||||
Psycopg extensions. ::
|
||||
Psycopg extensions.
|
||||
|
||||
.. doctest::
|
||||
:options: +NORMALIZE_WHITESPACE
|
||||
|
||||
>>> try:
|
||||
... cur.execute("SELECT * FROM barf")
|
||||
>>> except Exception, e:
|
||||
.... pass
|
||||
... except Exception, e:
|
||||
... pass
|
||||
|
||||
>>> e.pgcode
|
||||
>>> '42P01'
|
||||
'42P01'
|
||||
>>> print e.pgerror
|
||||
ERROR: relation "barf" does not exist
|
||||
LINE 1: SELECT * FROM barf
|
||||
|
|
|
@ -15,7 +15,7 @@ basic commands::
|
|||
# Connect to an existing database
|
||||
>>> conn = psycopg2.connect("dbname=test user=postgres")
|
||||
|
||||
# Open a curstor to perform database operations
|
||||
# Open a cursor to perform database operations
|
||||
>>> cur = conn.cursor()
|
||||
|
||||
# Execute a command: this creates a new table
|
||||
|
|
Loading…
Reference in New Issue
Block a user