described in this page is useful if you intend to *customise* the
adaptation rules.
-- The `~psycopg.types.TypeInfo` object allows to query type information from
- a database, which can be used by the adapters: for instance to make them
- able to decode arrays of base types or composite types.
-
-- The `Dumper` is the base object to perform conversion from a Python object
- to a `!bytes` string understood by PostgreSQL. The string returned
- *shouldn't be quoted*: the value will be passed to the database using
- functions such as :pq:`PQexecParams()` so quoting and quotes escaping is not
- necessary.
-
-- The `Loader` is the base object to perform the opposite operation: to read a
- `!bytes` string from PostgreSQL and create a Python object.
-
-`!Dumper` and `!Loader` are abstract classes: concrete classes must implement
-the `~Dumper.dump()` and `~Loader.load()` methods. Psycopg provides
-implementation for several builtin Python and PostgreSQL types.
-
-Psycopg provides adapters for several builtin types, which can be used as the
-base to build more complex ones: they all live in the `psycopg.types`
-package.
-
-
-Dumpers and loaders configuration
----------------------------------
-
-Dumpers and loaders can be registered on different scopes: globally, per
-`~psycopg.Connection`, per `~psycopg.Cursor`, so that adaptation rules can
-be customised for specific needs within the same application: in order to do
-so you can use the *context* parameter of `Dumper.register()` and
-`Loader.register()`.
-
-When a `!Connection` is created, it inherits the global adapters
-configuration; when a `!Cursor` is created it inherits its `!Connection`
-configuration.
-
-.. note::
-
- `!register()` is a class method on the base class, so if you
- subclass `!Dumper` or `!Loader` you should call the ``.register()`` on the
- class you created.
+- Adaptation configuration is performed by changing the
+ `~psycopg.proto.AdaptContext.adapters` object of objects implementing the
+ `~psycopg.proto.AdaptContext` protocols, for instance `~psycopg.Connection`
+ or `~psycopg.Cursor`.
+
+- Every context object derived from another context inherits its adapters
+ mapping: cursors created from a connection inherit the connection's
+ configuration. Connections obtain an adapters map from the global map
+ exposed as `psycopg.adapters`: changing the content of this object will
+ affect every connection created afterwards.
+
+ .. image:: ../pictures/adapt.svg
+ :align: center
+
+- The `!adapters` attribute are `AdaptersMap` instances, and contain the
+ mapping from Python types and `~psycopg.proto.Dumper` classes, and from
+ PostgreSQL oids to `~psycopg.proto.Loader` classes. Changing this mapping
+ (e.g. writing and registering your own adapters, or using a different
+ configuration of builtin adapters) affects how types are converted between
+ Python and PostgreSQL.
+
+ - Dumpers (objects implementing the `~psycopg.proto.Dumper` protocol) are
+ the objects used to perform the conversion from a Python object to a bytes
+ sequence in a format understood by PostgreSQL. The string returned
+ *shouldn't be quoted*: the value will be passed to the database using
+ functions such as :pq:`PQexecParams()` so quoting and quotes escaping is
+ not necessary. The dumper usually also suggests the server what type to
+ use, via its `~psycopg.proto.Dumper.oid` attribute.
+
+ - Loaders (objects implementing the `~psycopg.proto.Loader` protocol) are
+ the objects used to perform the opposite operation: reading a bytes
+ sequence from PostgreSQL and create a Python object out of it.
+
+ - Dumpers and loaders are instantiated on demand by a `~Transformer` object
+ when a query is executed.
Example: handling infinity date
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-------------------------------
Suppose you want to work with the "infinity" date which is available in
PostgreSQL but not handled by Python:
.. code:: python
from datetime import date
+
+ # Subclass existing adapters so that the base case is handled normally.
from psycopg.types.datetime import DateLoader, DateDumper
class InfDateDumper(DateDumper):
else:
return super().load(data)
+ # The new classes can be registered globally, on a connection, on a cursor
cur.adapters.register_dumper(date, InfDateDumper)
cur.adapters.register_loader("date", InfDateLoader)
cur.execute("select '2020-12-31'::date, 'infinity'::date").fetchone()
# (datetime.date(2020, 12, 31), datetime.date(9999, 12, 31))
+
+Example: PostgreSQL numeric to Python float
+-------------------------------------------
+
.. admonition:: TODO
- - Example: numeric to float
+ Write it
Dumpers and loaders life cycle
------------------------------
-Registering dumpers and loaders will instruct `!psycopg` to use them
+Registering dumpers and loaders will instruct Psycopg to use them
in the queries to follow, in the context where they have been registered.
-When a query is performed on a `!Cursor`, a `Transformer` object is created
-as a local context to manage conversions during the query, instantiating the
-required dumpers and loaders and dispatching the values to convert to the
-right instance.
+When a query is performed on a `~psycopg.Cursor`, a
+`~psycopg.adapt.Transformer` object is created as a local context to manage
+conversions during the query, instantiating the required dumpers and loaders
+and dispatching the values to convert to the right instance.
-- The `!Transformer` copies the adapters configuration from the `!Cursor`, thus
- inheriting all the changes made to the global configuration, the current
- `!Connection`, the `!Cursor`.
+- The `!Transformer` copies the adapters configuration from the `!Cursor`,
+ thus inheriting all the changes made to the global `psycopg.adapters`
+ configuration, the current `!Connection`, the `!Cursor`.
- For every Python type passed as query argument, the `!Transformer` will
instantiate a `!Dumper`. Usually all the objects of the same type will be
(for instance, a Python `int` might be better dumped as a PostgreSQL
:sql:`integer`, :sql:`bigint`, :sql:`smallint` according to its value).
-- According to the placeholder used (``%s``, ``%b``, ``%t``), Psycopg may
- pick a binary or a text dumper. When using the ``%s`` "`~PyFormat.AUTO`"
- format, if the same type has both a text and a binary dumper registered, the
- last one registered (using `Dumper.register()`) will be selected.
+- According to the placeholder used (``%s``, ``%b``, ``%t``), Psycopg may pick
+ a binary or a text dumper. When using the ``%s`` "`~PyFormat.AUTO`" format,
+ if the same type has both a text and a binary dumper registered, the last
+ one registered by `~AdaptersMap.register_dumper()` will be used.
+
+- Sometimes, just the Python type is not enough to infer the best PostgreSQL
+ type to use (for instance the PostgreSQL type of a Python list depends on
+ the objects it contains, whether to use an :sql:`integer` or :sql:`bigint`
+ depends on the number size...) In these cases the mechanism provided by
+ `~psycopg.proto.Dumper.get_key()` and `~psycopg.proto.Dumper.upgrade()` is
+ used.
- For every OID returned by the query, the `!Transformer` will instantiate a
`!Loader`. All the values with the same OID will be converted by the same
registered (for the right `~psycopg.pq.Format`) is used as query parameter.
If the query returns a data type whose OID doesn't have a `!Loader`, the
value will be returned as a string (or bytes string for binary types).
-
-
-Objects involved in types adaptation
-------------------------------------
-
-.. admonition:: TODO
-
- move to API section
-
-
-.. autoclass:: PyFormat
- :members:
-
-
-.. autoclass:: Dumper(cls, context=None)
-
- This is an abstract base class: subclasses *must* implement the `dump()`
- method and specify the `format`.
- They *may* implement `oid` (as attribute or property) in order to
- override the oid type oid; if not PostgreSQL will try to infer the type
- from the context, but this may fail in some contexts and may require a
- cast.
-
- :param cls: The type that will be managed by this dumper.
- :type cls: type
- :param context: The context where the transformation is performed. If not
- specified the conversion might be inaccurate, for instance it will not
- be possible to know the connection encoding or the server date format.
- :type context: `~psycopg.Connection`, `~psycopg.Cursor`, or `Transformer`
-
- .. attribute:: format
- :type: pq.Format
-
- The format this class dumps, `~Format.TEXT` or `~Format.BINARY`.
- This is a class attribute.
-
-
- .. automethod:: dump
-
- The format returned by dump shouldn't contain quotes or escaped
- values.
-
- .. automethod:: quote
-
- By default return the `dump()` value quoted and sanitised, so
- that the result can be used to build a SQL string. This works well
- for most types and you won't likely have to implement this method in a
- subclass.
-
- .. tip::
-
- This method will be used by `~psycopg.sql.Literal` to convert a
- value client-side.
-
- This method only makes sense for text dumpers; the result of calling
- it on a binary dumper is undefined. It might scratch your car, or burn
- your cake. Don't tell me I didn't warn you.
-
- .. autoattribute:: oid
-
- .. admonition:: todo
-
- Document how to find type OIDs in a database.
-
- .. automethod:: register(cls, context=None)
-
- You should call this method on the `Dumper` subclass you create,
- passing the Python type you want to dump as *cls*.
-
- If two dumpers of different `format` are registered for the same type,
- the last one registered will be chosen by default when the query
- doesn't specify a format (i.e. when the value is used with a ``%s``
- "`~Format.AUTO`" placeholder).
-
- :param cls: The type to manage.
- :type cls: `!type` or `!str`
- :param context: Where the dumper should be used. If `!None` the dumper
- will be used globally.
- :type context: `~psycopg.Connection`, `~psycopg.Cursor`, or `Transformer`
-
- If *cls* is specified as string it will be lazy-loaded, so that it
- will be possible to register it without importing it before. In this
- case it should be the fully qualified name of the object (e.g.
- ``"uuid.UUID"``).
-
-
-.. autoclass:: Loader(oid, context=None)
-
- This is an abstract base class: subclasses *must* implement the `load()`
- method and specify a `format`.
-
- :param oid: The type that will be managed by this dumper.
- :type oid: int
- :param context: The context where the transformation is performed. If not
- specified the conversion might be inaccurate, for instance it will not
- be possible to know the connection encoding or the server date format.
- :type context: `~psycopg.Connection`, `~psycopg.Cursor`, or `Transformer`
-
- .. attribute:: format
- :type: Format
-
- The format this class can load, `~Format.TEXT` or `~Format.BINARY`.
- This is a class attribute.
-
- .. automethod:: load
-
- .. automethod:: register(oid, context=None)
-
- You should call this method on the `Loader` subclass you create,
- passing the OID of the type you want to load as *oid* parameter.
-
- :param oid: The PostgreSQL OID to manage.
- :type oid: `!int`
- :param context: Where the loader should be used. If `!None` the loader
- will be used globally.
- :type context: `~psycopg.Connection`, `~psycopg.Cursor`, or `Transformer`
-
-
-.. autoclass:: Transformer(context=None)
-
- :param context: The context where the transformer should operate.
- :type context: `~psycopg.Connection`, `~psycopg.Cursor`, or `Transformer`
-
- TODO: finalise the interface of this object
--- /dev/null
+`adapt` -- Types adaptation
+===========================
+
+.. module:: psycopg.adapt
+
+The `!psycopg.adapt` module exposes a set of objects useful for the
+configuration of *data adaptation*, which is the conversion of Python objects
+to PostgreSQL data types and back.
+
+These objects are useful if you need to configure data adaptation, i.e.
+if you need to change the default way that Psycopg converts between types or
+if you want to adapt custom data types and objects. You don't need this object
+in the normal use of Psycopg.
+
+See :ref:`adaptation` for an overview of the Psycopg adaptation system.
+
+
+Dumpers and loaders
+-------------------
+
+.. autoclass:: Dumper(cls, context=None)
+
+ This is an abstract base class: subclasses *must* at least implement the
+ `dump()` method and specify the `format`.
+
+ The class implements the `~psycopg.proto.Dumper` protocol.
+
+ .. automethod:: dump
+
+ .. automethod:: quote
+
+ .. automethod:: get_key
+
+ .. automethod:: upgrade
+
+
+.. autoclass:: Loader(oid, context=None)
+
+ This is an abstract base class: subclasses *must* at least implement the
+ `!load()` method and specify a `format`.
+
+ The class implements the `~psycopg.proto.Loader` protocol.
+
+ .. automethod:: load
+
+
+Other objects used in adaptations
+---------------------------------
+
+.. autoclass:: PyFormat
+ :members:
+
+
+.. data:: psycopg.adapters
+
+ The global, default adapters map establishing how Python and PostgreSQL
+ types are converted into each other. This map is used as template when new
+ connections are created, using `psycopg.connect()`.
+
+ :type: `~psycopg.adapt.AdaptersMap`
+
+
+.. autoclass:: AdaptersMap
+
+ .. automethod:: register_dumper
+ .. automethod:: register_loader
+
+ .. attribute:: types
+
+ The object where to look up for types information (such as the mapping
+ between type names and oids in the specified context).
+
+ :type: `~psycopg.types.TypesRegistry`
+
+ .. automethod:: get_dumper
+ .. automethod:: get_loader
+
+
+.. autoclass:: Transformer(context=None)
+
+ :param context: The context where the transformer should operate.
+ :type context: `~psycopg.proto.AdaptContext`
-`errors` -- package exceptions
+`errors` -- Package exceptions
==============================
.. index::
=============
.. toctree::
- :maxdepth: 1
+ :maxdepth: 2
:caption: Contents:
connections
cursors
sql
errors
- pool
+ adapt
types
+ proto
+ pool
pq
--- /dev/null
+`proto` -- Psycopg abstract classes
+===================================
+
+TODO: rename to abc
+
+The module exposes Psycopg definitions which can be used for static type
+checking.
+
+.. module:: psycopg.proto
+
+.. autoclass:: Dumper(cls, context=None)
+
+ :param cls: The type that will be managed by this dumper.
+ :type cls: type
+ :param context: The context where the transformation is performed. If not
+ specified the conversion might be inaccurate, for instance it will not
+ be possible to know the connection encoding or the server date format.
+ :type context: `AdaptContext` or None
+
+ A partial implementation of this protocol (implementing everyting except
+ `dump()`) is available as `psycopg.adapt.Dumper`.
+
+ .. attribute:: format
+ :type: pq.Format
+
+ The format this class dumps, `~Format.TEXT` or `~Format.BINARY`.
+ This is a class attribute.
+
+ .. automethod:: dump
+
+ The format returned by dump shouldn't contain quotes or escaped
+ values.
+
+ .. automethod:: quote
+
+ .. tip::
+
+ This method will be used by `~psycopg.sql.Literal` to convert a
+ value client-side.
+
+ This method only makes sense for text dumpers; the result of calling
+ it on a binary dumper is undefined. It might scratch your car, or burn
+ your cake. Don't tell me I didn't warn you.
+
+ .. autoattribute:: oid
+
+ If the oid is not specified, PostgreSQL will try to infer the type
+ from the context, but this may fail in some contexts and may require a
+ cast (e.g. specifying :samp:`%s::{type}` for its placeholder).
+
+ .. admonition:: todo
+
+ Document how to find type OIDs in a database.
+
+ .. automethod:: get_key
+ .. automethod:: upgrade
+
+
+.. autoclass:: Loader(oid, context=None)
+
+ :param oid: The type that will be managed by this dumper.
+ :type oid: int
+ :param context: The context where the transformation is performed. If not
+ specified the conversion might be inaccurate, for instance it will not
+ be possible to know the connection encoding or the server date format.
+ :type context: `AdaptContext` or None
+
+ A partial implementation of this protocol (implementing everyting except
+ `load()`) is available as `psycopg.adapt.Loader`.
+
+ .. attribute:: format
+ :type: Format
+
+ The format this class can load, `~Format.TEXT` or `~Format.BINARY`.
+ This is a class attribute.
+
+ .. automethod:: load
+
+
+.. autoclass:: AdaptContext
+ :members:
.. _psycopg.types:
-`!types` -- types mapping and adaptation
-========================================
+`!types` -- Types information and adapters
+==========================================
.. module:: psycopg.types
Python is demanded to a `Loader`.
+.. autoclass:: TypesRegistry
+
+
The following `!TypeInfo` subclasses allow to fetch more specialised
information from certain class of PostgreSQL types and to create more
specialised adapters configurations.
objects when a query is executed.
The following table shows the default mapping between Python and PostgreSQL
-types:
+types. In case you need to customise the conversion you should take a look at
+:ref:`adaptation`.
TODO: complete table
--- /dev/null
+<mxfile host="Electron" modified="2021-07-12T13:26:05.192Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="kKU1DyIkJcQFc1Rxt__U" compressed="false" version="14.6.13" type="device">
+ <diagram id="THISp3X85jFCtBEH0bao" name="Page-1">
+ <mxGraphModel dx="675" dy="400" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="uy255Msn6vtulWmyCIR1-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontFamily=Courier New;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-29" target="uy255Msn6vtulWmyCIR1-11">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="280" y="210" as="sourcePoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Courier New;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-30" target="uy255Msn6vtulWmyCIR1-14">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="280" y="320" as="sourcePoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-39" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Courier New;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-11" target="uy255Msn6vtulWmyCIR1-14">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-11" value=".adapters" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="330" y="185" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Courier New;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-14" target="uy255Msn6vtulWmyCIR1-27">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-14" value=".adapters" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="330" y="285" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontFamily=Courier New;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-31" target="uy255Msn6vtulWmyCIR1-27">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="280" y="440" as="sourcePoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-18" value=".cursor()" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="220" y="220" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontFamily=Courier New;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-19" target="uy255Msn6vtulWmyCIR1-25">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Courier New;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-19" target="uy255Msn6vtulWmyCIR1-29">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-19" value="<b>psycopg</b><br><font face="Helvetica">module</font>" style="rounded=1;whiteSpace=wrap;html=1;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="160" y="75" width="120" height="50" as="geometry" />
+ </mxCell>
+ <UserObject label=".connect()" link="../api/connections.html" id="uy255Msn6vtulWmyCIR1-20">
+ <mxCell style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="220" y="125" width="80" height="20" as="geometry" />
+ </mxCell>
+ </UserObject>
+ <mxCell id="uy255Msn6vtulWmyCIR1-21" value=".execute()" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="220" y="320" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Courier New;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-25" target="uy255Msn6vtulWmyCIR1-11">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-25" value=".adapters" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="330" y="90" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-27" value=".adapters" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="330" y="385" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Courier New;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-29" target="uy255Msn6vtulWmyCIR1-30">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <UserObject label="<b>Connection</b><br><font face="Helvetica">object</font>" link="../api/connections.html" id="uy255Msn6vtulWmyCIR1-29">
+ <mxCell style="rounded=1;whiteSpace=wrap;html=1;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="160" y="170" width="120" height="50" as="geometry" />
+ </mxCell>
+ </UserObject>
+ <mxCell id="uy255Msn6vtulWmyCIR1-36" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Courier New;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-30" target="uy255Msn6vtulWmyCIR1-31">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-30" value="<b>Cursor</b><br><font face="Helvetica">object</font>" style="rounded=1;whiteSpace=wrap;html=1;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="160" y="270" width="120" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-31" value="<b>Transformer</b><br><font face="Helvetica">object</font>" style="rounded=1;whiteSpace=wrap;html=1;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="160" y="370" width="120" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-46" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Helvetica;endArrow=none;endFill=0;dashed=1;dashPattern=1 1;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-41">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="310" y="100" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-41" value="Has a" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Helvetica;" vertex="1" parent="1">
+ <mxGeometry x="300" y="55" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-45" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Helvetica;endArrow=none;endFill=0;dashed=1;dashPattern=1 1;startSize=4;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-42">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="220" y="150" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-42" value="Create" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Helvetica;" vertex="1" parent="1">
+ <mxGeometry x="150" y="130" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-47" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Helvetica;endArrow=none;endFill=0;dashed=1;dashPattern=1 1;" edge="1" parent="1" source="uy255Msn6vtulWmyCIR1-43">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="370" y="150" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="uy255Msn6vtulWmyCIR1-43" value="Copy" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Helvetica;" vertex="1" parent="1">
+ <mxGeometry x="394" y="130" width="40" height="20" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+</mxfile>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="285px" height="366px" viewBox="-0.5 -0.5 285 366" style="background-color: rgb(255, 255, 255);"><defs/><g><path d="M 130 140 L 173.63 140" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 178.88 140 L 171.88 143.5 L 173.63 140 L 171.88 136.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 130 240 L 173.63 240" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 178.88 240 L 171.88 243.5 L 173.63 240 L 171.88 236.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 220 150 L 220 223.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 220 228.88 L 216.5 221.88 L 220 223.63 L 223.5 221.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="180" y="130" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 140px; margin-left: 182px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">.adapters</div></div></div></foreignObject><text x="182" y="144" fill="#000000" font-family="Courier New" font-size="12px">.adapters</text></switch></g><path d="M 220 250 L 220 323.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 220 328.88 L 216.5 321.88 L 220 323.63 L 223.5 321.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="180" y="230" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 240px; margin-left: 182px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">.adapters</div></div></div></foreignObject><text x="182" y="244" fill="#000000" font-family="Courier New" font-size="12px">.adapters</text></switch></g><path d="M 130 340 L 173.63 340" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 178.88 340 L 171.88 343.5 L 173.63 340 L 171.88 336.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="70" y="165" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 175px; margin-left: 72px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">.cursor()</div></div></div></foreignObject><text x="72" y="179" fill="#000000" font-family="Courier New" font-size="12px">.cursor()</text></switch></g><path d="M 130 45 L 173.63 45" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 178.88 45 L 171.88 48.5 L 173.63 45 L 171.88 41.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 70 70 L 70 108.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 70 113.88 L 66.5 106.88 L 70 108.63 L 73.5 106.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="10" y="20" width="120" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 45px; margin-left: 11px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>psycopg</b><br /><font face="Helvetica">module</font></div></div></div></foreignObject><text x="70" y="49" fill="#000000" font-family="Courier New" font-size="12px" text-anchor="middle">psycopg...</text></switch></g><a xlink:href="../api/connections.html"><rect x="70" y="70" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 80px; margin-left: 72px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">.connect()</div></div></div></foreignObject><text x="72" y="84" fill="#000000" font-family="Courier New" font-size="12px">.connect()</text></switch></g></a><rect x="70" y="265" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 275px; margin-left: 72px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">.execute()</div></div></div></foreignObject><text x="72" y="279" fill="#000000" font-family="Courier New" font-size="12px">.execute()</text></switch></g><path d="M 220 55 L 220 123.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 220 128.88 L 216.5 121.88 L 220 123.63 L 223.5 121.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="180" y="35" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 45px; margin-left: 182px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">.adapters</div></div></div></foreignObject><text x="182" y="49" fill="#000000" font-family="Courier New" font-size="12px">.adapters</text></switch></g><rect x="180" y="330" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 340px; margin-left: 182px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">.adapters</div></div></div></foreignObject><text x="182" y="344" fill="#000000" font-family="Courier New" font-size="12px">.adapters</text></switch></g><path d="M 70 165 L 70 208.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 70 213.88 L 66.5 206.88 L 70 208.63 L 73.5 206.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><a xlink:href="../api/connections.html"><rect x="10" y="115" width="120" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 140px; margin-left: 11px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Connection</b><br /><font face="Helvetica">object</font></div></div></div></foreignObject><text x="70" y="144" fill="#000000" font-family="Courier New" font-size="12px" text-anchor="middle">Connection...</text></switch></g></a><path d="M 70 265 L 70 308.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 70 313.88 L 66.5 306.88 L 70 308.63 L 73.5 306.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="10" y="215" width="120" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 240px; margin-left: 11px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Cursor</b><br /><font face="Helvetica">object</font></div></div></div></foreignObject><text x="70" y="244" fill="#000000" font-family="Courier New" font-size="12px" text-anchor="middle">Cursor...</text></switch></g><rect x="10" y="315" width="120" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 340px; margin-left: 11px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Courier New; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Transformer</b><br /><font face="Helvetica">object</font></div></div></div></foreignObject><text x="70" y="344" fill="#000000" font-family="Courier New" font-size="12px" text-anchor="middle">Transformer...</text></switch></g><path d="M 167.14 20 L 160 45" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="1 1" pointer-events="stroke"/><rect x="150" y="0" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 10px; margin-left: 151px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Has a</div></div></div></foreignObject><text x="170" y="14" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Has a</text></switch></g><path d="M 40 89 L 70 95" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="1 1" pointer-events="stroke"/><rect x="0" y="75" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 85px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Create</div></div></div></foreignObject><text x="20" y="89" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Create</text></switch></g><path d="M 244 89.55 L 220 95" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="1 1" pointer-events="stroke"/><rect x="244" y="75" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 85px; margin-left: 245px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Copy</div></div></div></foreignObject><text x="264" y="89" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Copy</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file
from . import pq
from . import errors as e
from ._enums import PyFormat as PyFormat
-from .proto import AdaptContext, Dumper, Loader
+from .proto import Dumper, Loader
from ._cmodule import _psycopg
from ._typeinfo import TypesRegistry
RV = TypeVar("RV")
-class AdaptersMap(AdaptContext):
- """
- Map oids to Loaders and types to Dumpers.
+class AdaptersMap:
+ r"""
+ Establish how types should be converted between Python and PostgreSQL in
+ an `~psycopg.proto.AdaptContext`.
+
+ `!AdaptersMap` maps Python types to `~psycopg.adapt.Dumper` classes to
+ define how Python types are converted to PostgreSQL, and maps OIDs to
+ `~psycopg.adapt.Loader` classes to establish how query results are
+ converted to Python.
+
+ Every `!AdaptContext` object has an underlying `!AdaptersMap` defining how
+ types are converted in that context, exposed as the
+ `~psycopg.proto.AdaptContext.adapters` attribute: changing such map allows
+ to customise adaptation in a context without changing separated contexts.
+
+ When a context is created from another context (for instance when a
+ `~psycopg.Cursor` is created from a `~psycopg.Connection`), the parent's
+ `!adapters` are used as template for the child's `!adapters`, so that every
+ cursor created from the same connection use the connection's types
+ configuration, but separate connections have independent mappings. Once
+ created, `!AdaptersMap` are independent.
+
+ The connections adapters are initialised using a global `!AdptersMap`
+ template, exposed as `psycopg.adapters`: changing such mapping allows to
+ customise the type mapping for the entire application.
The object can start empty or copy from another object of the same class.
Copies are copy-on-write: if the maps are updated make a copy. This way
__module__ = "psycopg.adapt"
+ types: TypesRegistry
+
_dumpers: Dict[PyFormat, Dict[Union[type, str], Type[Dumper]]]
_loaders: List[Dict[int, Type[Loader]]]
- types: TypesRegistry
# Record if a dumper or loader has an optimised version.
_optimised: Dict[type, type] = {}
) -> None:
"""
Configure the context to use *dumper* to convert object of type *cls*.
+
+ If two dumpers with different `~Dumper.format` are registered for the
+ same type, the last one registered will be chosen when the query
+ doesn't specify a format (i.e. when the value is used with a ``%s``
+ "`~PyFormat.AUTO`" placeholder).
+
+ :param cls: The type to manage.
+ :param dumper: The dumper to register for *cls*.
+
+ If *cls* is specified as string it will be lazy-loaded, so that it
+ will be possible to register it without importing it before. In this
+ case it should be the fully qualified name of the object (e.g.
+ ``"uuid.UUID"``).
"""
if not isinstance(cls, (str, type)):
raise TypeError(
) -> None:
"""
Configure the context to use *loader* to convert data of oid *oid*.
+
+ :param oid: The PostgreSQL OID or type name to manage.
+ :param loader: The loar to register for *oid*.
+
+ If `oid` is specified as string, it refers to a type name, which is
+ looked up in the `types` registry. `
+
"""
if isinstance(oid, str):
oid = self.types[oid].oid
"""
An object that can adapt efficiently between Python and PostgreSQL.
- The life cycle of the object is the query, so it is assumed that stuff like
- the server version or connection encoding will not change. It can have its
- state so adapting several values of the same type can be optimised.
+ The life cycle of the object is the query, so it is assumed that attributes
+ such as the server version or the connection encoding will not change. The
+ object have its state so adapting several values of the same type can be
+ optimised.
+
"""
__module__ = "psycopg.adapt"
Container for the information about types in a database.
"""
+ __module__ = "psycopg.types"
+
def __init__(self, template: Optional["TypesRegistry"] = None):
self._by_oid: Dict[int, TypeInfo]
self._by_name: Dict[str, TypeInfo]
from abc import ABC, abstractmethod
from typing import Any, Optional, Type, Tuple, Union, TYPE_CHECKING
-from . import pq
+from . import pq, proto
from . import _adapters_map
from .proto import AdaptContext, Buffer as Buffer
from ._enums import PyFormat as PyFormat
from ._cmodule import _psycopg
if TYPE_CHECKING:
- from . import proto
from .connection import BaseConnection
AdaptersMap = _adapters_map.AdaptersMap
-class Dumper(ABC):
+class Dumper(proto.Dumper, ABC):
"""
Convert Python object of the type *cls* to PostgreSQL representation.
"""
- format: pq.Format
-
# A class-wide oid, which will be used by default by instances unless
# the subclass overrides it in init.
_oid: int = 0
+ oid: int
+ """The oid to pass to the server, if known."""
+
def __init__(self, cls: type, context: Optional[AdaptContext] = None):
self.cls = cls
self.connection: Optional["BaseConnection[Any]"] = (
context.connection if context else None
)
- self.oid: int = self._oid
- """The oid to pass to the server, if known."""
+ self.oid = self._oid
@abstractmethod
def dump(self, obj: Any) -> Buffer:
- """Convert the object *obj* to PostgreSQL representation."""
...
def quote(self, obj: Any) -> Buffer:
- """Convert the object *obj* to escaped representation."""
+ """
+ By default return the `dump()` value quoted and sanitised, so
+ that the result can be used to build a SQL string. This works well
+ for most types and you won't likely have to implement this method in a
+ subclass.
+ """
value = self.dump(obj)
if self.connection:
def get_key(
self, obj: Any, format: PyFormat
) -> Union[type, Tuple[type, ...]]:
- """Return an alternative key to upgrade the dumper to represent *obj*
-
- Normally the type of the object is all it takes to define how to dump
- the object to the database. In a few cases this is not enough. Example
-
- - Python int could be several Postgres types: int2, int4, int8, numeric
- - Python lists should be dumped according to the type they contain
- to convert them to e.g. array of strings, array of ints (which?...)
+ """
+ Implementation of the `~psycopg.proto.Dumper.get_key()` member of the
+ `~psycopg.proto.Dumper` protocol. Look at its definition for details.
- In these cases a Dumper can implement `get_key()` and return a new
- class, or sequence of classes, that can be used to indentify the same
- dumper again.
+ This implementation returns the *cls* passed in the constructor.
+ Subclasses needing to specialise the PostgreSQL type according to the
+ *value* of the object dumped (not only according to to its type)
+ should override this class.
- If a Dumper implements `get_key()` it should also implmement
- `upgrade()`.
"""
return self.cls
def upgrade(self, obj: Any, format: PyFormat) -> "Dumper":
- """Return a new dumper to manage *obj*.
+ """
+ Implementation of the `~psycopg.proto.Dumper.upgrade()` member of the
+ `~psycopg.proto.Dumper` protocol. Look at its definition for details.
- Once `Transformer.get_dumper()` has been notified that this Dumper
- class cannot handle *obj* itself it will invoke `upgrade()`, which
- should return a new `Dumper` instance, and will be reused for every
- objects for which `get_key()` returns the same result.
+ This implementation just returns *self*. If a subclass implements
+ `get_key()` it should probably override `!upgrade()` too.
"""
return self
from .pq import ConnStatus, ExecStatus, TransactionStatus, Format
from .sql import Composable
from .rows import Row, RowFactory, tuple_row, TupleRow
-from .proto import AdaptContext, ConnectionType, Params, PQGen, PQGenConn
+from .proto import ConnectionType, Params, PQGen, PQGenConn
from .proto import Query, RV
from .compat import asynccontextmanager
from .cursor import Cursor, AsyncCursor
NotifyHandler = Callable[[Notify], None]
-class BaseConnection(AdaptContext, Generic[Row]):
+class BaseConnection(Generic[Row]):
"""
Base class for different types of connections.
"""
A context describing how types are adapted.
- Example of AdaptContext are connections, cursors, transformers.
+ Example of `~AdaptContext` are `~psycopg.Connection`, `~psycopg.Cursor`,
+ `~psycopg.adapt.Transformer`, `~psycopg.adapt.AdaptersMap`.
+
+ Note that this is a `~typing.Protocol`, so objects implementing
+ `!AdaptContext` don't need to explicitly inherit from this class.
+
"""
@property
def adapters(self) -> "AdaptersMap":
+ """The adapters configuration that this object uses."""
...
@property
def connection(self) -> Optional["BaseConnection[Any]"]:
+ """The connection used by this object, if available.
+
+ :rtype: `~psycopg.Connection` or `~psycopg.AsyncConnection` or `!None`
+ """
...
class Dumper(Protocol):
+ """
+ Convert Python objects of type *cls* to PostgreSQL representation.
+ """
+
format: pq.Format
oid: int
+ """The oid to pass to the server, if known; 0 otherwise."""
def __init__(self, cls: type, context: Optional[AdaptContext] = None):
...
def dump(self, obj: Any) -> Buffer:
+ """Convert the object *obj* to PostgreSQL representation."""
...
def quote(self, obj: Any) -> Buffer:
+ """Convert the object *obj* to escaped representation."""
...
def get_key(self, obj: Any, format: PyFormat) -> DumperKey:
+ """Return an alternative key to upgrade the dumper to represent *obj*.
+
+ :param obj: The object to convert
+ :param format: The format to convert to
+
+ Normally the type of the object is all it takes to define how to dump
+ the object to the database. For instance, a Python `~datetime.date` can
+ be simply converted into a PostgreSQL :sql:`date`.
+
+ In a few cases, just the type is not enough. For example:
+
+ - A Python `~datetime.datetime` could be represented as a
+ :sql:`timestamptz` or a :sql:`timestamp`, according to whether it
+ specifies a `!tzinfo` or not.
+
+ - A Python int could be stored as several Postgres types: int2, int4,
+ int8, numeric. If a type too small is used, it may result in an
+ overflow. If a type too large is used, PostgreSQL may not want to
+ cast it to a smaller type.
+
+ - Python lists should be dumped according to the type they contain to
+ convert them to e.g. array of strings, array of ints (and which
+ size of int?...)
+
+ In these cases, a dumper can implement `!get_key()` and return a new
+ class, or sequence of classes, that can be used to indentify the same
+ dumper again. If the mechanism is not needed, the method should return
+ the same *cls* object passed in the constructor.
+
+ If a dumper implements `get_key()` it should also implmement
+ `upgrade()`.
+
+ """
...
def upgrade(self, obj: Any, format: PyFormat) -> "Dumper":
+ """Return a new dumper to manage *obj*.
+
+ :param obj: The object to convert
+ :param format: The format to convert to
+
+ Once `Transformer.get_dumper()` has been notified by `get_key()` that
+ this Dumper class cannot handle *obj* itself, it will invoke
+ `!upgrade()`, which should return a new `Dumper` instance, which will
+ be reused for every objects for which `!get_key()` returns the same
+ result.
+ """
...
class Loader(Protocol):
+ """
+ Convert PostgreSQL objects with OID *oid* to Python objects.
+ """
+
format: pq.Format
def __init__(self, oid: int, context: Optional[AdaptContext] = None):
from .. import _typeinfo
-TypeInfo = _typeinfo.TypeInfo # exported here
+# Exposed here
+TypeInfo = _typeinfo.TypeInfo
+TypesRegistry = _typeinfo.TypesRegistry
"""
An object that can adapt efficiently between Python and PostgreSQL.
- The life cycle of the object is the query, so it is assumed that stuff like
- the server version or connection encoding will not change. It can have its
- state so adapting several values of the same type can use optimisations.
+ The life cycle of the object is the query, so it is assumed that attributes
+ such as the server version or the connection encoding will not change. The
+ object have its state so adapting several values of the same type can be
+ optimised.
+
"""
cdef readonly object connection