mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-11-01 00:07:36 +03:00 
			
		
		
		
	Merge branch 'master' into feature/replication-protocol-c-connection-object
This commit is contained in:
		
						commit
						cb7032554e
					
				
							
								
								
									
										6
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								NEWS
									
									
									
									
									
								
							|  | @ -6,7 +6,10 @@ What's new in psycopg 2.7 | ||||||
| 
 | 
 | ||||||
| New features: | New features: | ||||||
| 
 | 
 | ||||||
| - Added `~psycopg2.extensions.parse_dsn()` function (:ticket:`#321`). | - Added `~psycopg2.extensions.parse_dsn()` and | ||||||
|  |   `~psycopg2.extensions.make_dsn()` functions (:tickets:`#321, #363`). | ||||||
|  |   `~psycopg2.connect()` now can take both *dsn* and keyword arguments, merging | ||||||
|  |   them together. | ||||||
| - Added `~psycopg2.__libpq_version__` and | - Added `~psycopg2.__libpq_version__` and | ||||||
|   `~psycopg2.extensions.libpq_version()` to inspect the version of the |   `~psycopg2.extensions.libpq_version()` to inspect the version of the | ||||||
|   ``libpq`` library the module was compiled/loaded with |   ``libpq`` library the module was compiled/loaded with | ||||||
|  | @ -27,6 +30,7 @@ What's new in psycopg 2.6.2 | ||||||
| - Raise `!NotSupportedError` on unhandled server response status | - Raise `!NotSupportedError` on unhandled server response status | ||||||
|   (:ticket:`#352`). |   (:ticket:`#352`). | ||||||
| - Fixed `!PersistentConnectionPool` on Python 3 (:ticket:`#348`). | - Fixed `!PersistentConnectionPool` on Python 3 (:ticket:`#348`). | ||||||
|  | - Fixed `!errorcodes.lookup` initialization thread-safety (:ticket:`#382`). | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| What's new in psycopg 2.6.1 | What's new in psycopg 2.6.1 | ||||||
|  |  | ||||||
|  | @ -270,7 +270,7 @@ wasting resources. | ||||||
| 
 | 
 | ||||||
| A simple application could poll the connection from time to time to check if | A simple application could poll the connection from time to time to check if | ||||||
| something new has arrived. A better strategy is to use some I/O completion | something new has arrived. A better strategy is to use some I/O completion | ||||||
| function such as :py:func:`~select.select` to sleep until awaken from the kernel when there is | function such as :py:func:`~select.select` to sleep until awakened by the kernel when there is | ||||||
| some data to read on the connection, thereby using no CPU unless there is | some data to read on the connection, thereby using no CPU unless there is | ||||||
| something to read:: | something to read:: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,17 +12,12 @@ | ||||||
| The module contains a few objects and function extending the minimum set of | The module contains a few objects and function extending the minimum set of | ||||||
| functionalities defined by the |DBAPI|_. | functionalities defined by the |DBAPI|_. | ||||||
| 
 | 
 | ||||||
| .. function:: parse_dsn(dsn) | Classes definitions | ||||||
|  | ------------------- | ||||||
| 
 | 
 | ||||||
|     Parse connection string into a dictionary of keywords and values. | Instances of these classes are usually returned by factory functions or | ||||||
| 
 | attributes. Their definitions are exposed here to allow subclassing, | ||||||
|     Uses libpq's ``PQconninfoParse`` to parse the string according to | introspection etc. | ||||||
|     accepted format(s) and check for supported keywords. |  | ||||||
| 
 |  | ||||||
|     Example:: |  | ||||||
| 
 |  | ||||||
|         >>> psycopg2.extensions.parse_dsn('dbname=test user=postgres password=secret') |  | ||||||
|         {'password': 'secret', 'user': 'postgres', 'dbname': 'test'} |  | ||||||
| 
 | 
 | ||||||
| .. function:: make_dsn(**kwargs) | .. function:: make_dsn(**kwargs) | ||||||
| 
 | 
 | ||||||
|  | @ -56,6 +51,7 @@ functionalities defined by the |DBAPI|_. | ||||||
| 
 | 
 | ||||||
|     For a complete description of the class, see `connection`. |     For a complete description of the class, see `connection`. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| .. class:: cursor(conn, name=None) | .. class:: cursor(conn, name=None) | ||||||
| 
 | 
 | ||||||
|     It is the class usually returned by the `connection.cursor()` |     It is the class usually returned by the `connection.cursor()` | ||||||
|  | @ -66,6 +62,7 @@ functionalities defined by the |DBAPI|_. | ||||||
| 
 | 
 | ||||||
|     For a complete description of the class, see `cursor`. |     For a complete description of the class, see `cursor`. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| .. class:: lobject(conn [, oid [, mode [, new_oid [, new_file ]]]]) | .. class:: lobject(conn [, oid [, mode [, new_oid [, new_file ]]]]) | ||||||
| 
 | 
 | ||||||
|     Wrapper for a PostgreSQL large object. See :ref:`large-objects` for an |     Wrapper for a PostgreSQL large object. See :ref:`large-objects` for an | ||||||
|  | @ -222,39 +219,6 @@ functionalities defined by the |DBAPI|_. | ||||||
|         server versions. |         server versions. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| .. autofunction:: set_wait_callback(f) |  | ||||||
| 
 |  | ||||||
|     .. versionadded:: 2.2.0 |  | ||||||
| 
 |  | ||||||
| .. autofunction:: get_wait_callback() |  | ||||||
| 
 |  | ||||||
|     .. versionadded:: 2.2.0 |  | ||||||
| 
 |  | ||||||
| .. function:: libpq_version() |  | ||||||
| 
 |  | ||||||
|     Return the version number of the ``libpq`` dynamic library loaded as an |  | ||||||
|     integer, in the same format of `~connection.server_version`. |  | ||||||
| 
 |  | ||||||
|     Raise `~psycopg2.NotSupportedError` if the ``psycopg2`` module was |  | ||||||
|     compiled with a ``libpq`` version lesser than 9.1 (which can be detected |  | ||||||
|     by the `~psycopg2.__libpq_version__` constant). |  | ||||||
| 
 |  | ||||||
|     .. seealso:: libpq docs for `PQlibVersion()`__. |  | ||||||
| 
 |  | ||||||
|         .. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION |  | ||||||
| 
 |  | ||||||
| .. function:: quote_ident(str, scope) |  | ||||||
| 
 |  | ||||||
|     Return quoted identifier according to PostgreSQL quoting rules. |  | ||||||
| 
 |  | ||||||
|     The *scope* must be a `connection` or a `cursor`, the underlying |  | ||||||
|     connection encoding is used for any necessary character conversion. |  | ||||||
| 
 |  | ||||||
|     Requires libpq >= 9.0. |  | ||||||
| 
 |  | ||||||
|     .. seealso:: libpq docs for `PQescapeIdentifier()`__ |  | ||||||
| 
 |  | ||||||
|         .. __: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER |  | ||||||
| 
 | 
 | ||||||
| .. _sql-adaptation-objects: | .. _sql-adaptation-objects: | ||||||
| 
 | 
 | ||||||
|  | @ -514,6 +478,106 @@ The module exports a few exceptions in addition to the :ref:`standard ones | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | .. _coroutines-functions: | ||||||
|  | 
 | ||||||
|  | Coroutines support functions | ||||||
|  | ---------------------------- | ||||||
|  | 
 | ||||||
|  | These functions are used to set and retrieve the callback function for | ||||||
|  | :ref:`cooperation with coroutine libraries <green-support>`. | ||||||
|  | 
 | ||||||
|  | .. versionadded:: 2.2.0 | ||||||
|  | 
 | ||||||
|  | .. autofunction:: set_wait_callback(f) | ||||||
|  | 
 | ||||||
|  | .. autofunction:: get_wait_callback() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Other functions | ||||||
|  | --------------- | ||||||
|  | 
 | ||||||
|  | .. function:: libpq_version() | ||||||
|  | 
 | ||||||
|  |     Return the version number of the ``libpq`` dynamic library loaded as an | ||||||
|  |     integer, in the same format of `~connection.server_version`. | ||||||
|  | 
 | ||||||
|  |     Raise `~psycopg2.NotSupportedError` if the ``psycopg2`` module was | ||||||
|  |     compiled with a ``libpq`` version lesser than 9.1 (which can be detected | ||||||
|  |     by the `~psycopg2.__libpq_version__` constant). | ||||||
|  | 
 | ||||||
|  |     .. versionadded:: 2.7 | ||||||
|  | 
 | ||||||
|  |     .. seealso:: libpq docs for `PQlibVersion()`__. | ||||||
|  | 
 | ||||||
|  |         .. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | .. function:: make_dsn(dsn=None, \*\*kwargs) | ||||||
|  | 
 | ||||||
|  |     Create a valid connection string from arguments. | ||||||
|  | 
 | ||||||
|  |     Put together the arguments in *kwargs* into a connection string. If *dsn* | ||||||
|  |     is specified too, merge the arguments coming from both the sources. If the | ||||||
|  |     same argument name is specified in both the sources, the *kwargs* value | ||||||
|  |     overrides the *dsn* value. | ||||||
|  | 
 | ||||||
|  |     The input arguments are validated: the output should always be a valid | ||||||
|  |     connection string (as far as `parse_dsn()` is concerned). If not raise | ||||||
|  |     `~psycopg2.ProgrammingError`. | ||||||
|  | 
 | ||||||
|  |     Example:: | ||||||
|  | 
 | ||||||
|  |         >>> from psycopg2.extensions import make_dsn | ||||||
|  |         >>> make_dsn('dbname=foo host=example.com', password="s3cr3t") | ||||||
|  |         'host=example.com password=s3cr3t dbname=foo' | ||||||
|  | 
 | ||||||
|  |     .. versionadded:: 2.7 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | .. function:: parse_dsn(dsn) | ||||||
|  | 
 | ||||||
|  |     Parse connection string into a dictionary of keywords and values. | ||||||
|  | 
 | ||||||
|  |     Parsing is delegated to the libpq: different versions of the client | ||||||
|  |     library may support different formats or parameters (for example, | ||||||
|  |     `connection URIs`__ are only supported from libpq 9.2).  Raise | ||||||
|  |     `~psycopg2.ProgrammingError` if the *dsn* is not valid. | ||||||
|  | 
 | ||||||
|  |     .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING | ||||||
|  | 
 | ||||||
|  |     Example:: | ||||||
|  | 
 | ||||||
|  |         >>> from psycopg2.extensions import parse_dsn | ||||||
|  |         >>> parse_dsn('dbname=test user=postgres password=secret') | ||||||
|  |         {'password': 'secret', 'user': 'postgres', 'dbname': 'test'} | ||||||
|  |         >>> parse_dsn("postgresql://someone@example.com/somedb?connect_timeout=10") | ||||||
|  |         {'host': 'example.com', 'user': 'someone', 'dbname': 'somedb', 'connect_timeout': '10'} | ||||||
|  | 
 | ||||||
|  |     .. versionadded:: 2.7 | ||||||
|  | 
 | ||||||
|  |     .. seealso:: libpq docs for `PQconninfoParse()`__. | ||||||
|  | 
 | ||||||
|  |         .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFOPARSE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | .. function:: quote_ident(str, scope) | ||||||
|  | 
 | ||||||
|  |     Return quoted identifier according to PostgreSQL quoting rules. | ||||||
|  | 
 | ||||||
|  |     The *scope* must be a `connection` or a `cursor`, the underlying | ||||||
|  |     connection encoding is used for any necessary character conversion. | ||||||
|  | 
 | ||||||
|  |     Requires libpq >= 9.0. | ||||||
|  | 
 | ||||||
|  |     .. versionadded:: 2.7 | ||||||
|  | 
 | ||||||
|  |     .. seealso:: libpq docs for `PQescapeIdentifier()`__ | ||||||
|  | 
 | ||||||
|  |         .. __: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| .. index:: | .. index:: | ||||||
|     pair: Isolation level; Constants |     pair: Isolation level; Constants | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,37 +17,34 @@ The module interface respects the standard defined in the |DBAPI|_. | ||||||
|     single: DSN (Database Source Name) |     single: DSN (Database Source Name) | ||||||
| 
 | 
 | ||||||
| .. function:: | .. function:: | ||||||
|     connect(dsn, connection_factory=None, cursor_factory=None, async=False) |     connect(dsn=None, connection_factory=None, cursor_factory=None, async=False, \*\*kwargs) | ||||||
|     connect(\*\*kwargs, connection_factory=None, cursor_factory=None, async=False) |  | ||||||
| 
 | 
 | ||||||
|     Create a new database session and return a new `connection` object. |     Create a new database session and return a new `connection` object. | ||||||
| 
 | 
 | ||||||
|     The connection parameters can be specified either as a `libpq connection |     The connection parameters can be specified as a `libpq connection | ||||||
|     string`__ using the *dsn* parameter:: |     string`__ using the *dsn* parameter:: | ||||||
| 
 | 
 | ||||||
|         conn = psycopg2.connect("dbname=test user=postgres password=secret") |         conn = psycopg2.connect("dbname=test user=postgres password=secret") | ||||||
| 
 | 
 | ||||||
|     or using a set of keyword arguments:: |     or using a set of keyword arguments:: | ||||||
| 
 | 
 | ||||||
|         conn = psycopg2.connect(database="test", user="postgres", password="secret") |         conn = psycopg2.connect(dbname"test", user="postgres", password="secret") | ||||||
| 
 | 
 | ||||||
|     The two call styles are mutually exclusive: you cannot specify connection |     or using a mix of both: if the same parameter name is specified in both | ||||||
|     parameters as keyword arguments together with a connection string; only |     sources, the *kwargs* value will have precedence over the *dsn* value. | ||||||
|     the parameters not needed for the database connection (*i.e.* |     Note that either the *dsn* or at least one connection-related keyword | ||||||
|     *connection_factory*, *cursor_factory*, and *async*) are supported |     argument is required. | ||||||
|     together with the *dsn* argument. |  | ||||||
| 
 | 
 | ||||||
|     The basic connection parameters are: |     The basic connection parameters are: | ||||||
| 
 | 
 | ||||||
|     - `!dbname` -- the database name (only in the *dsn* string) |     - `!dbname` -- the database name (`!database` is a deprecated alias) | ||||||
|     - `!database` -- the database name (only as keyword argument) |  | ||||||
|     - `!user` -- user name used to authenticate |     - `!user` -- user name used to authenticate | ||||||
|     - `!password` -- password used to authenticate |     - `!password` -- password used to authenticate | ||||||
|     - `!host` -- database host address (defaults to UNIX socket if not provided) |     - `!host` -- database host address (defaults to UNIX socket if not provided) | ||||||
|     - `!port` -- connection port number (defaults to 5432 if not provided) |     - `!port` -- connection port number (defaults to 5432 if not provided) | ||||||
| 
 | 
 | ||||||
|     Any other connection parameter supported by the client library/server can |     Any other connection parameter supported by the client library/server can | ||||||
|     be passed either in the connection string or as keywords. The PostgreSQL |     be passed either in the connection string or as a keyword. The PostgreSQL | ||||||
|     documentation contains the complete list of the `supported parameters`__. |     documentation contains the complete list of the `supported parameters`__. | ||||||
|     Also note that the same parameters can be passed to the client library |     Also note that the same parameters can be passed to the client library | ||||||
|     using `environment variables`__. |     using `environment variables`__. | ||||||
|  | @ -76,6 +73,9 @@ The module interface respects the standard defined in the |DBAPI|_. | ||||||
|     .. versionchanged:: 2.5 |     .. versionchanged:: 2.5 | ||||||
|         added the *cursor_factory* parameter. |         added the *cursor_factory* parameter. | ||||||
| 
 | 
 | ||||||
|  |     .. versionchanged:: 2.7 | ||||||
|  |         both *dsn* and keyword arguments can be specified. | ||||||
|  | 
 | ||||||
|     .. seealso:: |     .. seealso:: | ||||||
| 
 | 
 | ||||||
|         - `~psycopg2.extensions.parse_dsn` |         - `~psycopg2.extensions.parse_dsn` | ||||||
|  | @ -89,8 +89,8 @@ The module interface respects the standard defined in the |DBAPI|_. | ||||||
| 
 | 
 | ||||||
|     .. extension:: |     .. extension:: | ||||||
| 
 | 
 | ||||||
|         The parameters *connection_factory* and *async* are Psycopg extensions |         The non-connection-related keyword parameters are Psycopg extensions | ||||||
|         to the |DBAPI|. |         to the |DBAPI|_. | ||||||
| 
 | 
 | ||||||
| .. data:: apilevel | .. data:: apilevel | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -81,12 +81,12 @@ else: | ||||||
|     del Decimal, Adapter |     del Decimal, Adapter | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def connect(dsn=None, | def connect(dsn=None, connection_factory=None, cursor_factory=None, | ||||||
|         connection_factory=None, cursor_factory=None, async=False, **kwargs): |         async=False, **kwargs): | ||||||
|     """ |     """ | ||||||
|     Create a new database connection. |     Create a new database connection. | ||||||
| 
 | 
 | ||||||
|     The connection parameters can be specified either as a string: |     The connection parameters can be specified as a string: | ||||||
| 
 | 
 | ||||||
|         conn = psycopg2.connect("dbname=test user=postgres password=secret") |         conn = psycopg2.connect("dbname=test user=postgres password=secret") | ||||||
| 
 | 
 | ||||||
|  | @ -94,9 +94,9 @@ def connect(dsn=None, | ||||||
| 
 | 
 | ||||||
|         conn = psycopg2.connect(database="test", user="postgres", password="secret") |         conn = psycopg2.connect(database="test", user="postgres", password="secret") | ||||||
| 
 | 
 | ||||||
|     The basic connection parameters are: |     Or as a mix of both. The basic connection parameters are: | ||||||
| 
 | 
 | ||||||
|     - *dbname*: the database name (only in dsn string) |     - *dbname*: the database name | ||||||
|     - *database*: the database name (only as keyword argument) |     - *database*: the database name (only as keyword argument) | ||||||
|     - *user*: user name used to authenticate |     - *user*: user name used to authenticate | ||||||
|     - *password*: password used to authenticate |     - *password*: password used to authenticate | ||||||
|  | @ -116,7 +116,11 @@ def connect(dsn=None, | ||||||
|     library: the list of supported parameters depends on the library version. |     library: the list of supported parameters depends on the library version. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
|     conn = _connect(dsn, connection_factory, async, **kwargs) |     if dsn is None and not kwargs: | ||||||
|  |         raise TypeError('missing dsn and no parameters') | ||||||
|  | 
 | ||||||
|  |     dsn = _ext.make_dsn(dsn, **kwargs) | ||||||
|  |     conn = _connect(dsn, connection_factory=connection_factory, async=async) | ||||||
|     if cursor_factory is not None: |     if cursor_factory is not None: | ||||||
|         conn.cursor_factory = cursor_factory |         conn.cursor_factory = cursor_factory | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,11 +38,17 @@ def lookup(code, _cache={}): | ||||||
|         return _cache[code] |         return _cache[code] | ||||||
| 
 | 
 | ||||||
|     # Generate the lookup map at first usage. |     # Generate the lookup map at first usage. | ||||||
|  |     tmp = {} | ||||||
|     for k, v in globals().iteritems(): |     for k, v in globals().iteritems(): | ||||||
|         if isinstance(v, str) and len(v) in (2, 5): |         if isinstance(v, str) and len(v) in (2, 5): | ||||||
|             _cache[v] = k |             tmp[v] = k | ||||||
| 
 | 
 | ||||||
|     return lookup(code) |     assert tmp | ||||||
|  | 
 | ||||||
|  |     # Atomic update, to avoid race condition on import (bug #382) | ||||||
|  |     _cache.update(tmp) | ||||||
|  | 
 | ||||||
|  |     return _cache[code] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # autogenerated data: do not edit below this point. | # autogenerated data: do not edit below this point. | ||||||
|  |  | ||||||
|  | @ -32,6 +32,9 @@ This module holds all the extensions to the DBAPI-2.0 provided by psycopg. | ||||||
| # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public | # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public | ||||||
| # License for more details. | # License for more details. | ||||||
| 
 | 
 | ||||||
|  | import re as _re | ||||||
|  | import sys as _sys | ||||||
|  | 
 | ||||||
| from psycopg2._psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT | from psycopg2._psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT | ||||||
| from psycopg2._psycopg import TIME, DATE, INTERVAL, DECIMAL | from psycopg2._psycopg import TIME, DATE, INTERVAL, DECIMAL | ||||||
| from psycopg2._psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY | from psycopg2._psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY | ||||||
|  | @ -56,8 +59,8 @@ try: | ||||||
| except ImportError: | except ImportError: | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
| from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid, libpq_version | from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor | ||||||
| from psycopg2._psycopg import parse_dsn, make_dsn, quote_ident | from psycopg2._psycopg import lobject, Xid, libpq_version, parse_dsn, quote_ident | ||||||
| from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type | from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type | ||||||
| from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column | from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column | ||||||
| 
 | 
 | ||||||
|  | @ -101,7 +104,6 @@ TRANSACTION_STATUS_INTRANS = 2 | ||||||
| TRANSACTION_STATUS_INERROR = 3 | TRANSACTION_STATUS_INERROR = 3 | ||||||
| TRANSACTION_STATUS_UNKNOWN = 4 | TRANSACTION_STATUS_UNKNOWN = 4 | ||||||
| 
 | 
 | ||||||
| import sys as _sys |  | ||||||
| 
 | 
 | ||||||
| # Return bytes from a string | # Return bytes from a string | ||||||
| if _sys.version_info[0] < 3: | if _sys.version_info[0] < 3: | ||||||
|  | @ -111,6 +113,7 @@ else: | ||||||
|     def b(s): |     def b(s): | ||||||
|         return s.encode('utf8') |         return s.encode('utf8') | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def register_adapter(typ, callable): | def register_adapter(typ, callable): | ||||||
|     """Register 'callable' as an ISQLQuote adapter for type 'typ'.""" |     """Register 'callable' as an ISQLQuote adapter for type 'typ'.""" | ||||||
|     adapters[(typ, ISQLQuote)] = callable |     adapters[(typ, ISQLQuote)] = callable | ||||||
|  | @ -154,6 +157,53 @@ class NoneAdapter(object): | ||||||
|         return _null |         return _null | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def make_dsn(dsn=None, **kwargs): | ||||||
|  |     """Convert a set of keywords into a connection strings.""" | ||||||
|  |     if dsn is None and not kwargs: | ||||||
|  |         return '' | ||||||
|  | 
 | ||||||
|  |     # If no kwarg is specified don't mung the dsn, but verify it | ||||||
|  |     if not kwargs: | ||||||
|  |         parse_dsn(dsn) | ||||||
|  |         return dsn | ||||||
|  | 
 | ||||||
|  |     # Override the dsn with the parameters | ||||||
|  |     if 'database' in kwargs: | ||||||
|  |         if 'dbname' in kwargs: | ||||||
|  |             raise TypeError( | ||||||
|  |                 "you can't specify both 'database' and 'dbname' arguments") | ||||||
|  |         kwargs['dbname'] = kwargs.pop('database') | ||||||
|  | 
 | ||||||
|  |     if dsn is not None: | ||||||
|  |         tmp = parse_dsn(dsn) | ||||||
|  |         tmp.update(kwargs) | ||||||
|  |         kwargs = tmp | ||||||
|  | 
 | ||||||
|  |     dsn = " ".join(["%s=%s" % (k, _param_escape(str(v))) | ||||||
|  |         for (k, v) in kwargs.iteritems()]) | ||||||
|  | 
 | ||||||
|  |     # verify that the returned dsn is valid | ||||||
|  |     parse_dsn(dsn) | ||||||
|  | 
 | ||||||
|  |     return dsn | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _param_escape(s, | ||||||
|  |         re_escape=_re.compile(r"([\\'])"), | ||||||
|  |         re_space=_re.compile(r'\s')): | ||||||
|  |     """ | ||||||
|  |     Apply the escaping rule required by PQconnectdb | ||||||
|  |     """ | ||||||
|  |     if not s: | ||||||
|  |         return "''" | ||||||
|  | 
 | ||||||
|  |     s = re_escape.sub(r'\\\1', s) | ||||||
|  |     if re_space.search(s): | ||||||
|  |         s = "'" + s + "'" | ||||||
|  | 
 | ||||||
|  |     return s | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # Create default json typecasters for PostgreSQL 9.2 oids | # Create default json typecasters for PostgreSQL 9.2 oids | ||||||
| from psycopg2._json import register_default_json, register_default_jsonb | from psycopg2._json import register_default_json, register_default_jsonb | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -28,7 +28,7 @@ old code while porting to psycopg 2. Import it as follows:: | ||||||
| # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public | # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public | ||||||
| # License for more details. | # License for more details. | ||||||
| 
 | 
 | ||||||
| import _psycopg as _2psycopg | import psycopg2._psycopg as _2psycopg | ||||||
| from psycopg2.extensions import cursor as _2cursor | from psycopg2.extensions import cursor as _2cursor | ||||||
| from psycopg2.extensions import connection as _2connection | from psycopg2.extensions import connection as _2connection | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -199,7 +199,8 @@ exit: | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #define psyco_parse_dsn_doc "parse_dsn(dsn) -> dict" | #define psyco_parse_dsn_doc \ | ||||||
|  | "parse_dsn(dsn) -> dict -- parse a connection string into parameters" | ||||||
| 
 | 
 | ||||||
| PyObject * | PyObject * | ||||||
| psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs) | psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs) | ||||||
|  | @ -219,7 +220,7 @@ psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs) | ||||||
|     options = PQconninfoParse(Bytes_AS_STRING(dsn), &err); |     options = PQconninfoParse(Bytes_AS_STRING(dsn), &err); | ||||||
|     if (options == NULL) { |     if (options == NULL) { | ||||||
|         if (err != NULL) { |         if (err != NULL) { | ||||||
|             PyErr_Format(ProgrammingError, "error parsing the dsn: %s", err); |             PyErr_Format(ProgrammingError, "invalid dsn: %s", err); | ||||||
|             PQfreemem(err); |             PQfreemem(err); | ||||||
|         } else { |         } else { | ||||||
|             PyErr_SetString(OperationalError, "PQconninfoParse() failed"); |             PyErr_SetString(OperationalError, "PQconninfoParse() failed"); | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ import test_replication | ||||||
| import test_copy | import test_copy | ||||||
| import test_cursor | import test_cursor | ||||||
| import test_dates | import test_dates | ||||||
|  | import test_errcodes | ||||||
| import test_extras_dictcursor | import test_extras_dictcursor | ||||||
| import test_green | import test_green | ||||||
| import test_lobject | import test_lobject | ||||||
|  | @ -73,6 +74,7 @@ def test_suite(): | ||||||
|     suite.addTest(test_copy.test_suite()) |     suite.addTest(test_copy.test_suite()) | ||||||
|     suite.addTest(test_cursor.test_suite()) |     suite.addTest(test_cursor.test_suite()) | ||||||
|     suite.addTest(test_dates.test_suite()) |     suite.addTest(test_dates.test_suite()) | ||||||
|  |     suite.addTest(test_errcodes.test_suite()) | ||||||
|     suite.addTest(test_extras_dictcursor.test_suite()) |     suite.addTest(test_extras_dictcursor.test_suite()) | ||||||
|     suite.addTest(test_green.test_suite()) |     suite.addTest(test_green.test_suite()) | ||||||
|     suite.addTest(test_lobject.test_suite()) |     suite.addTest(test_lobject.test_suite()) | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ from StringIO import StringIO | ||||||
| import psycopg2 | import psycopg2 | ||||||
| import psycopg2.errorcodes | import psycopg2.errorcodes | ||||||
| import psycopg2.extensions | import psycopg2.extensions | ||||||
|  | ext = psycopg2.extensions | ||||||
| 
 | 
 | ||||||
| from testutils import unittest, decorate_all_tests, skip_if_no_superuser | from testutils import unittest, decorate_all_tests, skip_if_no_superuser | ||||||
| from testutils import skip_before_postgres, skip_after_postgres, skip_before_libpq | from testutils import skip_before_postgres, skip_after_postgres, skip_before_libpq | ||||||
|  | @ -125,7 +126,7 @@ class ConnectionTests(ConnectingTestCase): | ||||||
|         if self.conn.server_version >= 90300: |         if self.conn.server_version >= 90300: | ||||||
|             cur.execute("set client_min_messages=debug1") |             cur.execute("set client_min_messages=debug1") | ||||||
|         for i in range(0, 100, 10): |         for i in range(0, 100, 10): | ||||||
|             sql = " ".join(["create temp table table%d (id serial);" % j for j in range(i, i+10)]) |             sql = " ".join(["create temp table table%d (id serial);" % j for j in range(i, i + 10)]) | ||||||
|             cur.execute(sql) |             cur.execute(sql) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(50, len(conn.notices)) |         self.assertEqual(50, len(conn.notices)) | ||||||
|  | @ -151,7 +152,7 @@ class ConnectionTests(ConnectingTestCase): | ||||||
| 
 | 
 | ||||||
|         # not limited, but no error |         # not limited, but no error | ||||||
|         for i in range(0, 100, 10): |         for i in range(0, 100, 10): | ||||||
|             sql = " ".join(["create temp table table2_%d (id serial);" % j for j in range(i, i+10)]) |             sql = " ".join(["create temp table table2_%d (id serial);" % j for j in range(i, i + 10)]) | ||||||
|             cur.execute(sql) |             cur.execute(sql) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(len([n for n in conn.notices if 'CREATE TABLE' in n]), |         self.assertEqual(len([n for n in conn.notices if 'CREATE TABLE' in n]), | ||||||
|  | @ -172,7 +173,7 @@ class ConnectionTests(ConnectingTestCase): | ||||||
|         self.assert_(self.conn.server_version) |         self.assert_(self.conn.server_version) | ||||||
| 
 | 
 | ||||||
|     def test_protocol_version(self): |     def test_protocol_version(self): | ||||||
|         self.assert_(self.conn.protocol_version in (2,3), |         self.assert_(self.conn.protocol_version in (2, 3), | ||||||
|             self.conn.protocol_version) |             self.conn.protocol_version) | ||||||
| 
 | 
 | ||||||
|     def test_tpc_unsupported(self): |     def test_tpc_unsupported(self): | ||||||
|  | @ -252,7 +253,7 @@ class ConnectionTests(ConnectingTestCase): | ||||||
|         t1.start() |         t1.start() | ||||||
|         i = 1 |         i = 1 | ||||||
|         for i in range(1000): |         for i in range(1000): | ||||||
|             cur.execute("select %s;",(i,)) |             cur.execute("select %s;", (i,)) | ||||||
|             conn.commit() |             conn.commit() | ||||||
|             while conn.notices: |             while conn.notices: | ||||||
|                 notices.append((1, conn.notices.pop())) |                 notices.append((1, conn.notices.pop())) | ||||||
|  | @ -313,16 +314,15 @@ class ConnectionTests(ConnectingTestCase): | ||||||
| class ParseDsnTestCase(ConnectingTestCase): | class ParseDsnTestCase(ConnectingTestCase): | ||||||
|     def test_parse_dsn(self): |     def test_parse_dsn(self): | ||||||
|         from psycopg2 import ProgrammingError |         from psycopg2 import ProgrammingError | ||||||
|         from psycopg2.extensions import parse_dsn |  | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(parse_dsn('dbname=test user=tester password=secret'), |         self.assertEqual(ext.parse_dsn('dbname=test user=tester password=secret'), | ||||||
|                          dict(user='tester', password='secret', dbname='test'), |                          dict(user='tester', password='secret', dbname='test'), | ||||||
|                          "simple DSN parsed") |                          "simple DSN parsed") | ||||||
| 
 | 
 | ||||||
|         self.assertRaises(ProgrammingError, parse_dsn, |         self.assertRaises(ProgrammingError, ext.parse_dsn, | ||||||
|                           "dbname=test 2 user=tester password=secret") |                           "dbname=test 2 user=tester password=secret") | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(parse_dsn("dbname='test 2' user=tester password=secret"), |         self.assertEqual(ext.parse_dsn("dbname='test 2' user=tester password=secret"), | ||||||
|                          dict(user='tester', password='secret', dbname='test 2'), |                          dict(user='tester', password='secret', dbname='test 2'), | ||||||
|                          "DSN with quoting parsed") |                          "DSN with quoting parsed") | ||||||
| 
 | 
 | ||||||
|  | @ -332,7 +332,7 @@ class ParseDsnTestCase(ConnectingTestCase): | ||||||
|         raised = False |         raised = False | ||||||
|         try: |         try: | ||||||
|             # unterminated quote after dbname: |             # unterminated quote after dbname: | ||||||
|             parse_dsn("dbname='test 2 user=tester password=secret") |             ext.parse_dsn("dbname='test 2 user=tester password=secret") | ||||||
|         except ProgrammingError, e: |         except ProgrammingError, e: | ||||||
|             raised = True |             raised = True | ||||||
|             self.assertTrue(str(e).find('secret') < 0, |             self.assertTrue(str(e).find('secret') < 0, | ||||||
|  | @ -343,16 +343,14 @@ class ParseDsnTestCase(ConnectingTestCase): | ||||||
| 
 | 
 | ||||||
|     @skip_before_libpq(9, 2) |     @skip_before_libpq(9, 2) | ||||||
|     def test_parse_dsn_uri(self): |     def test_parse_dsn_uri(self): | ||||||
|         from psycopg2.extensions import parse_dsn |         self.assertEqual(ext.parse_dsn('postgresql://tester:secret@/test'), | ||||||
| 
 |  | ||||||
|         self.assertEqual(parse_dsn('postgresql://tester:secret@/test'), |  | ||||||
|                          dict(user='tester', password='secret', dbname='test'), |                          dict(user='tester', password='secret', dbname='test'), | ||||||
|                          "valid URI dsn parsed") |                          "valid URI dsn parsed") | ||||||
| 
 | 
 | ||||||
|         raised = False |         raised = False | ||||||
|         try: |         try: | ||||||
|             # extra '=' after port value |             # extra '=' after port value | ||||||
|             parse_dsn(dsn='postgresql://tester:secret@/test?port=1111=x') |             ext.parse_dsn(dsn='postgresql://tester:secret@/test?port=1111=x') | ||||||
|         except psycopg2.ProgrammingError, e: |         except psycopg2.ProgrammingError, e: | ||||||
|             raised = True |             raised = True | ||||||
|             self.assertTrue(str(e).find('secret') < 0, |             self.assertTrue(str(e).find('secret') < 0, | ||||||
|  | @ -362,24 +360,91 @@ class ParseDsnTestCase(ConnectingTestCase): | ||||||
|         self.assertTrue(raised, "ProgrammingError raised due to invalid URI") |         self.assertTrue(raised, "ProgrammingError raised due to invalid URI") | ||||||
| 
 | 
 | ||||||
|     def test_unicode_value(self): |     def test_unicode_value(self): | ||||||
|         from psycopg2.extensions import parse_dsn |  | ||||||
|         snowman = u"\u2603" |         snowman = u"\u2603" | ||||||
|         d = parse_dsn('dbname=' + snowman) |         d = ext.parse_dsn('dbname=' + snowman) | ||||||
|         if sys.version_info[0] < 3: |         if sys.version_info[0] < 3: | ||||||
|             self.assertEqual(d['dbname'], snowman.encode('utf8')) |             self.assertEqual(d['dbname'], snowman.encode('utf8')) | ||||||
|         else: |         else: | ||||||
|             self.assertEqual(d['dbname'], snowman) |             self.assertEqual(d['dbname'], snowman) | ||||||
| 
 | 
 | ||||||
|     def test_unicode_key(self): |     def test_unicode_key(self): | ||||||
|         from psycopg2.extensions import parse_dsn |  | ||||||
|         snowman = u"\u2603" |         snowman = u"\u2603" | ||||||
|         self.assertRaises(psycopg2.ProgrammingError, parse_dsn, |         self.assertRaises(psycopg2.ProgrammingError, ext.parse_dsn, | ||||||
|             snowman + '=' + snowman) |             snowman + '=' + snowman) | ||||||
| 
 | 
 | ||||||
|     def test_bad_param(self): |     def test_bad_param(self): | ||||||
|         from psycopg2.extensions import parse_dsn |         self.assertRaises(TypeError, ext.parse_dsn, None) | ||||||
|         self.assertRaises(TypeError, parse_dsn, None) |         self.assertRaises(TypeError, ext.parse_dsn, 42) | ||||||
|         self.assertRaises(TypeError, parse_dsn, 42) | 
 | ||||||
|  | 
 | ||||||
|  | class MakeDsnTestCase(ConnectingTestCase): | ||||||
|  |     def assertDsnEqual(self, dsn1, dsn2): | ||||||
|  |         self.assertEqual(set(dsn1.split()), set(dsn2.split())) | ||||||
|  | 
 | ||||||
|  |     def test_empty_arguments(self): | ||||||
|  |         self.assertEqual(ext.make_dsn(), '') | ||||||
|  | 
 | ||||||
|  |     def test_empty_string(self): | ||||||
|  |         dsn = ext.make_dsn('') | ||||||
|  |         self.assertEqual(dsn, '') | ||||||
|  | 
 | ||||||
|  |     def test_params_validation(self): | ||||||
|  |         self.assertRaises(psycopg2.ProgrammingError, | ||||||
|  |             ext.make_dsn, 'dbnamo=a') | ||||||
|  |         self.assertRaises(psycopg2.ProgrammingError, | ||||||
|  |             ext.make_dsn, dbnamo='a') | ||||||
|  |         self.assertRaises(psycopg2.ProgrammingError, | ||||||
|  |             ext.make_dsn, 'dbname=a', nosuchparam='b') | ||||||
|  | 
 | ||||||
|  |     def test_empty_param(self): | ||||||
|  |         dsn = ext.make_dsn(dbname='sony', password='') | ||||||
|  |         self.assertDsnEqual(dsn, "dbname=sony password=''") | ||||||
|  | 
 | ||||||
|  |     def test_escape(self): | ||||||
|  |         dsn = ext.make_dsn(dbname='hello world') | ||||||
|  |         self.assertEqual(dsn, "dbname='hello world'") | ||||||
|  | 
 | ||||||
|  |         dsn = ext.make_dsn(dbname=r'back\slash') | ||||||
|  |         self.assertEqual(dsn, r"dbname=back\\slash") | ||||||
|  | 
 | ||||||
|  |         dsn = ext.make_dsn(dbname="quo'te") | ||||||
|  |         self.assertEqual(dsn, r"dbname=quo\'te") | ||||||
|  | 
 | ||||||
|  |         dsn = ext.make_dsn(dbname="with\ttab") | ||||||
|  |         self.assertEqual(dsn, "dbname='with\ttab'") | ||||||
|  | 
 | ||||||
|  |         dsn = ext.make_dsn(dbname=r"\every thing'") | ||||||
|  |         self.assertEqual(dsn, r"dbname='\\every thing\''") | ||||||
|  | 
 | ||||||
|  |     def test_database_is_a_keyword(self): | ||||||
|  |         self.assertEqual(ext.make_dsn(database='sigh'), "dbname=sigh") | ||||||
|  | 
 | ||||||
|  |     def test_params_merging(self): | ||||||
|  |         dsn = ext.make_dsn('dbname=foo host=bar', host='baz') | ||||||
|  |         self.assertDsnEqual(dsn, 'dbname=foo host=baz') | ||||||
|  | 
 | ||||||
|  |         dsn = ext.make_dsn('dbname=foo', user='postgres') | ||||||
|  |         self.assertDsnEqual(dsn, 'dbname=foo user=postgres') | ||||||
|  | 
 | ||||||
|  |     def test_no_dsn_munging(self): | ||||||
|  |         dsnin = 'dbname=a host=b user=c password=d' | ||||||
|  |         dsn = ext.make_dsn(dsnin) | ||||||
|  |         self.assertEqual(dsn, dsnin) | ||||||
|  | 
 | ||||||
|  |     @skip_before_libpq(9, 2) | ||||||
|  |     def test_url_is_cool(self): | ||||||
|  |         url = 'postgresql://tester:secret@/test?application_name=wat' | ||||||
|  |         dsn = ext.make_dsn(url) | ||||||
|  |         self.assertEqual(dsn, url) | ||||||
|  | 
 | ||||||
|  |         dsn = ext.make_dsn(url, application_name='woot') | ||||||
|  |         self.assertDsnEqual(dsn, | ||||||
|  |             'dbname=test user=tester password=secret application_name=woot') | ||||||
|  | 
 | ||||||
|  |         self.assertRaises(psycopg2.ProgrammingError, | ||||||
|  |             ext.make_dsn, 'postgresql://tester:secret@/test?nosuch=param') | ||||||
|  |         self.assertRaises(psycopg2.ProgrammingError, | ||||||
|  |             ext.make_dsn, url, nosuch="param") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class IsolationLevelsTestCase(ConnectingTestCase): | class IsolationLevelsTestCase(ConnectingTestCase): | ||||||
|  | @ -587,7 +652,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase): | ||||||
|             cnn.close() |             cnn.close() | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         gids = [ r[0] for r in cur ] |         gids = [r[0] for r in cur] | ||||||
|         for gid in gids: |         for gid in gids: | ||||||
|             cur.execute("rollback prepared %s;", (gid,)) |             cur.execute("rollback prepared %s;", (gid,)) | ||||||
|         cnn.close() |         cnn.close() | ||||||
|  | @ -761,13 +826,13 @@ class ConnectionTwoPhaseTests(ConnectingTestCase): | ||||||
|     def test_status_after_recover(self): |     def test_status_after_recover(self): | ||||||
|         cnn = self.connect() |         cnn = self.connect() | ||||||
|         self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status) |         self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status) | ||||||
|         xns = cnn.tpc_recover() |         cnn.tpc_recover() | ||||||
|         self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status) |         self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status) | ||||||
| 
 | 
 | ||||||
|         cur = cnn.cursor() |         cur = cnn.cursor() | ||||||
|         cur.execute("select 1") |         cur.execute("select 1") | ||||||
|         self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status) |         self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status) | ||||||
|         xns = cnn.tpc_recover() |         cnn.tpc_recover() | ||||||
|         self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status) |         self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status) | ||||||
| 
 | 
 | ||||||
|     def test_recovered_xids(self): |     def test_recovered_xids(self): | ||||||
|  | @ -789,12 +854,12 @@ class ConnectionTwoPhaseTests(ConnectingTestCase): | ||||||
| 
 | 
 | ||||||
|         cnn = self.connect() |         cnn = self.connect() | ||||||
|         xids = cnn.tpc_recover() |         xids = cnn.tpc_recover() | ||||||
|         xids = [ xid for xid in xids if xid.database == dbname ] |         xids = [xid for xid in xids if xid.database == dbname] | ||||||
|         xids.sort(key=attrgetter('gtrid')) |         xids.sort(key=attrgetter('gtrid')) | ||||||
| 
 | 
 | ||||||
|         # check the values returned |         # check the values returned | ||||||
|         self.assertEqual(len(okvals), len(xids)) |         self.assertEqual(len(okvals), len(xids)) | ||||||
|         for (xid, (gid, prepared, owner, database)) in zip (xids, okvals): |         for (xid, (gid, prepared, owner, database)) in zip(xids, okvals): | ||||||
|             self.assertEqual(xid.gtrid, gid) |             self.assertEqual(xid.gtrid, gid) | ||||||
|             self.assertEqual(xid.prepared, prepared) |             self.assertEqual(xid.prepared, prepared) | ||||||
|             self.assertEqual(xid.owner, owner) |             self.assertEqual(xid.owner, owner) | ||||||
|  | @ -825,8 +890,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase): | ||||||
|             cnn.close() |             cnn.close() | ||||||
| 
 | 
 | ||||||
|             cnn = self.connect() |             cnn = self.connect() | ||||||
|             xids = [ xid for xid in cnn.tpc_recover() |             xids = [x for x in cnn.tpc_recover() if x.database == dbname] | ||||||
|                 if xid.database == dbname ] |  | ||||||
|             self.assertEqual(1, len(xids)) |             self.assertEqual(1, len(xids)) | ||||||
|             xid = xids[0] |             xid = xids[0] | ||||||
|             self.assertEqual(xid.format_id, fid) |             self.assertEqual(xid.format_id, fid) | ||||||
|  | @ -847,8 +911,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase): | ||||||
|             cnn.close() |             cnn.close() | ||||||
| 
 | 
 | ||||||
|             cnn = self.connect() |             cnn = self.connect() | ||||||
|             xids = [ xid for xid in cnn.tpc_recover() |             xids = [x for x in cnn.tpc_recover() if x.database == dbname] | ||||||
|                 if xid.database == dbname ] |  | ||||||
|             self.assertEqual(1, len(xids)) |             self.assertEqual(1, len(xids)) | ||||||
|             xid = xids[0] |             xid = xids[0] | ||||||
|             self.assertEqual(xid.format_id, None) |             self.assertEqual(xid.format_id, None) | ||||||
|  | @ -893,8 +956,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase): | ||||||
|         cnn.tpc_begin(x1) |         cnn.tpc_begin(x1) | ||||||
|         cnn.tpc_prepare() |         cnn.tpc_prepare() | ||||||
|         cnn.reset() |         cnn.reset() | ||||||
|         xid = [ xid for xid in cnn.tpc_recover() |         xid = [x for x in cnn.tpc_recover() if x.database == dbname][0] | ||||||
|             if xid.database == dbname ][0] |  | ||||||
|         self.assertEqual(10, xid.format_id) |         self.assertEqual(10, xid.format_id) | ||||||
|         self.assertEqual('uni', xid.gtrid) |         self.assertEqual('uni', xid.gtrid) | ||||||
|         self.assertEqual('code', xid.bqual) |         self.assertEqual('code', xid.bqual) | ||||||
|  | @ -909,8 +971,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase): | ||||||
|         cnn.tpc_prepare() |         cnn.tpc_prepare() | ||||||
|         cnn.reset() |         cnn.reset() | ||||||
| 
 | 
 | ||||||
|         xid = [ xid for xid in cnn.tpc_recover() |         xid = [x for x in cnn.tpc_recover() if x.database == dbname][0] | ||||||
|             if xid.database == dbname ][0] |  | ||||||
|         self.assertEqual(None, xid.format_id) |         self.assertEqual(None, xid.format_id) | ||||||
|         self.assertEqual('transaction-id', xid.gtrid) |         self.assertEqual('transaction-id', xid.gtrid) | ||||||
|         self.assertEqual(None, xid.bqual) |         self.assertEqual(None, xid.bqual) | ||||||
|  | @ -929,7 +990,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase): | ||||||
|         cnn.reset() |         cnn.reset() | ||||||
| 
 | 
 | ||||||
|         xids = cnn.tpc_recover() |         xids = cnn.tpc_recover() | ||||||
|         xid = [ xid for xid in xids if xid.database == dbname ][0] |         xid = [x for x in xids if x.database == dbname][0] | ||||||
|         self.assertEqual(None, xid.format_id) |         self.assertEqual(None, xid.format_id) | ||||||
|         self.assertEqual('dict-connection', xid.gtrid) |         self.assertEqual('dict-connection', xid.gtrid) | ||||||
|         self.assertEqual(None, xid.bqual) |         self.assertEqual(None, xid.bqual) | ||||||
|  |  | ||||||
							
								
								
									
										65
									
								
								tests/test_errcodes.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										65
									
								
								tests/test_errcodes.py
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,65 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | 
 | ||||||
|  | # test_errcodes.py - unit test for psycopg2.errcodes module | ||||||
|  | # | ||||||
|  | # Copyright (C) 2015 Daniele Varrazzo  <daniele.varrazzo@gmail.com> | ||||||
|  | # | ||||||
|  | # psycopg2 is free software: you can redistribute it and/or modify it | ||||||
|  | # under the terms of the GNU Lesser General Public License as published | ||||||
|  | # by the Free Software Foundation, either version 3 of the License, or | ||||||
|  | # (at your option) any later version. | ||||||
|  | # | ||||||
|  | # In addition, as a special exception, the copyright holders give | ||||||
|  | # permission to link this program with the OpenSSL library (or with | ||||||
|  | # modified versions of OpenSSL that use the same license as OpenSSL), | ||||||
|  | # and distribute linked combinations including the two. | ||||||
|  | # | ||||||
|  | # You must obey the GNU Lesser General Public License in all respects for | ||||||
|  | # all of the code used other than OpenSSL. | ||||||
|  | # | ||||||
|  | # psycopg2 is distributed in the hope that it will be useful, but WITHOUT | ||||||
|  | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||||
|  | # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public | ||||||
|  | # License for more details. | ||||||
|  | 
 | ||||||
|  | from testutils import unittest, ConnectingTestCase | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     reload | ||||||
|  | except NameError: | ||||||
|  |     from imp import reload | ||||||
|  | 
 | ||||||
|  | from threading import Thread | ||||||
|  | from psycopg2 import errorcodes | ||||||
|  | 
 | ||||||
|  | class ErrocodeTests(ConnectingTestCase): | ||||||
|  |     def test_lookup_threadsafe(self): | ||||||
|  | 
 | ||||||
|  |         # Increase if it does not fail with KeyError | ||||||
|  |         MAX_CYCLES = 2000 | ||||||
|  | 
 | ||||||
|  |         errs = [] | ||||||
|  |         def f(pg_code='40001'): | ||||||
|  |             try: | ||||||
|  |                 errorcodes.lookup(pg_code) | ||||||
|  |             except Exception, e: | ||||||
|  |                 errs.append(e) | ||||||
|  | 
 | ||||||
|  |         for __ in xrange(MAX_CYCLES): | ||||||
|  |             reload(errorcodes) | ||||||
|  |             (t1, t2) = (Thread(target=f), Thread(target=f)) | ||||||
|  |             (t1.start(), t2.start()) | ||||||
|  |             (t1.join(), t2.join()) | ||||||
|  | 
 | ||||||
|  |             if errs: | ||||||
|  |                 self.fail( | ||||||
|  |                     "raised %s errors in %s cycles (first is %s %s)" % ( | ||||||
|  |                         len(errs), MAX_CYCLES, | ||||||
|  |                         errs[0].__class__.__name__, errs[0])) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_suite(): | ||||||
|  |     return unittest.TestLoader().loadTestsFromName(__name__) | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     unittest.main() | ||||||
|  | @ -31,11 +31,13 @@ from testutils import ConnectingTestCase, skip_copy_if_green, script_to_py3 | ||||||
| 
 | 
 | ||||||
| import psycopg2 | import psycopg2 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class ConnectTestCase(unittest.TestCase): | class ConnectTestCase(unittest.TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.args = None |         self.args = None | ||||||
|         def connect_stub(*args, **kwargs): | 
 | ||||||
|             self.args = psycopg2.parse_args(*args, **kwargs) |         def conect_stub(dsn, connection_factory=None, async=False): | ||||||
|  |             self.args = (dsn, connection_factory, async) | ||||||
| 
 | 
 | ||||||
|         self._connect_orig = psycopg2._connect |         self._connect_orig = psycopg2._connect | ||||||
|         psycopg2._connect = connect_stub |         psycopg2._connect = connect_stub | ||||||
|  | @ -43,6 +45,9 @@ class ConnectTestCase(unittest.TestCase): | ||||||
|     def tearDown(self): |     def tearDown(self): | ||||||
|         psycopg2._connect = self._connect_orig |         psycopg2._connect = self._connect_orig | ||||||
| 
 | 
 | ||||||
|  |     def assertDsnEqual(self, dsn1, dsn2): | ||||||
|  |         self.assertEqual(set(dsn1.split()), set(dsn2.split())) | ||||||
|  | 
 | ||||||
|     def test_there_has_to_be_something(self): |     def test_there_has_to_be_something(self): | ||||||
|         self.assertRaises(TypeError, psycopg2.connect) |         self.assertRaises(TypeError, psycopg2.connect) | ||||||
|         self.assertRaises(TypeError, psycopg2.connect, |         self.assertRaises(TypeError, psycopg2.connect, | ||||||
|  | @ -57,8 +62,8 @@ class ConnectTestCase(unittest.TestCase): | ||||||
|         self.assertEqual(self.args[2], False) |         self.assertEqual(self.args[2], False) | ||||||
| 
 | 
 | ||||||
|     def test_dsn(self): |     def test_dsn(self): | ||||||
|         psycopg2.connect('dbname=blah x=y') |         psycopg2.connect('dbname=blah host=y') | ||||||
|         self.assertEqual(self.args[0], 'dbname=blah x=y') |         self.assertEqual(self.args[0], 'dbname=blah host=y') | ||||||
|         self.assertEqual(self.args[1], None) |         self.assertEqual(self.args[1], None) | ||||||
|         self.assertEqual(self.args[2], False) |         self.assertEqual(self.args[2], False) | ||||||
| 
 | 
 | ||||||
|  | @ -83,39 +88,31 @@ class ConnectTestCase(unittest.TestCase): | ||||||
|         self.assertEqual(len(self.args[0].split()), 4) |         self.assertEqual(len(self.args[0].split()), 4) | ||||||
| 
 | 
 | ||||||
|     def test_generic_keywords(self): |     def test_generic_keywords(self): | ||||||
|         psycopg2.connect(foo='bar') |         psycopg2.connect(options='stuff') | ||||||
|         self.assertEqual(self.args[0], 'foo=bar') |         self.assertEqual(self.args[0], 'options=stuff') | ||||||
| 
 | 
 | ||||||
|     def test_factory(self): |     def test_factory(self): | ||||||
|         def f(dsn, async=False): |         def f(dsn, async=False): | ||||||
|             pass |             pass | ||||||
| 
 | 
 | ||||||
|         psycopg2.connect(database='foo', bar='baz', connection_factory=f) |         psycopg2.connect(database='foo', host='baz', connection_factory=f) | ||||||
|         dsn = " %s " % self.args[0] |         self.assertDsnEqual(self.args[0], 'dbname=foo host=baz') | ||||||
|         self.assertIn(" dbname=foo ", dsn) |  | ||||||
|         self.assertIn(" bar=baz ", dsn) |  | ||||||
|         self.assertEqual(self.args[1], f) |         self.assertEqual(self.args[1], f) | ||||||
|         self.assertEqual(self.args[2], False) |         self.assertEqual(self.args[2], False) | ||||||
| 
 | 
 | ||||||
|         psycopg2.connect("dbname=foo bar=baz", connection_factory=f) |         psycopg2.connect("dbname=foo host=baz", connection_factory=f) | ||||||
|         dsn = " %s " % self.args[0] |         self.assertDsnEqual(self.args[0], 'dbname=foo host=baz') | ||||||
|         self.assertIn(" dbname=foo ", dsn) |  | ||||||
|         self.assertIn(" bar=baz ", dsn) |  | ||||||
|         self.assertEqual(self.args[1], f) |         self.assertEqual(self.args[1], f) | ||||||
|         self.assertEqual(self.args[2], False) |         self.assertEqual(self.args[2], False) | ||||||
| 
 | 
 | ||||||
|     def test_async(self): |     def test_async(self): | ||||||
|         psycopg2.connect(database='foo', bar='baz', async=1) |         psycopg2.connect(database='foo', host='baz', async=1) | ||||||
|         dsn = " %s " % self.args[0] |         self.assertDsnEqual(self.args[0], 'dbname=foo host=baz') | ||||||
|         self.assertIn(" dbname=foo ", dsn) |  | ||||||
|         self.assertIn(" bar=baz ", dsn) |  | ||||||
|         self.assertEqual(self.args[1], None) |         self.assertEqual(self.args[1], None) | ||||||
|         self.assert_(self.args[2]) |         self.assert_(self.args[2]) | ||||||
| 
 | 
 | ||||||
|         psycopg2.connect("dbname=foo bar=baz", async=True) |         psycopg2.connect("dbname=foo host=baz", async=True) | ||||||
|         dsn = " %s " % self.args[0] |         self.assertDsnEqual(self.args[0], 'dbname=foo host=baz') | ||||||
|         self.assertIn(" dbname=foo ", dsn) |  | ||||||
|         self.assertIn(" bar=baz ", dsn) |  | ||||||
|         self.assertEqual(self.args[1], None) |         self.assertEqual(self.args[1], None) | ||||||
|         self.assert_(self.args[2]) |         self.assert_(self.args[2]) | ||||||
| 
 | 
 | ||||||
|  | @ -127,9 +124,7 @@ class ConnectTestCase(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     def test_empty_param(self): |     def test_empty_param(self): | ||||||
|         psycopg2.connect(database='sony', password='') |         psycopg2.connect(database='sony', password='') | ||||||
|         dsn = " %s " % self.args[0] |         self.assertDsnEqual(self.args[0], "dbname=sony password=''") | ||||||
|         self.assertIn(" dbname=sony ", dsn) |  | ||||||
|         self.assertIn(" password='' ", dsn) |  | ||||||
| 
 | 
 | ||||||
|     def test_escape(self): |     def test_escape(self): | ||||||
|         psycopg2.connect(database='hello world') |         psycopg2.connect(database='hello world') | ||||||
|  | @ -147,13 +142,12 @@ class ConnectTestCase(unittest.TestCase): | ||||||
|         psycopg2.connect(database=r"\every thing'") |         psycopg2.connect(database=r"\every thing'") | ||||||
|         self.assertEqual(self.args[0], r"dbname='\\every thing\''") |         self.assertEqual(self.args[0], r"dbname='\\every thing\''") | ||||||
| 
 | 
 | ||||||
|     def test_no_kwargs_swallow(self): |     def test_params_merging(self): | ||||||
|         self.assertRaises(TypeError, |         psycopg2.connect('dbname=foo', database='bar') | ||||||
|             psycopg2.connect, 'dbname=foo', database='foo') |         self.assertEqual(self.args[0], 'dbname=bar') | ||||||
|         self.assertRaises(TypeError, | 
 | ||||||
|             psycopg2.connect, 'dbname=foo', user='postgres') |         psycopg2.connect('dbname=foo', user='postgres') | ||||||
|         self.assertRaises(TypeError, |         self.assertDsnEqual(self.args[0], 'dbname=foo user=postgres') | ||||||
|             psycopg2.connect, 'dbname=foo', no_such_param='meh') |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ExceptionsTestCase(ConnectingTestCase): | class ExceptionsTestCase(ConnectingTestCase): | ||||||
|  | @ -219,7 +213,8 @@ class ExceptionsTestCase(ConnectingTestCase): | ||||||
|         self.assertEqual(diag.sqlstate, '42P01') |         self.assertEqual(diag.sqlstate, '42P01') | ||||||
| 
 | 
 | ||||||
|         del diag |         del diag | ||||||
|         gc.collect(); gc.collect() |         gc.collect() | ||||||
|  |         gc.collect() | ||||||
|         assert(w() is None) |         assert(w() is None) | ||||||
| 
 | 
 | ||||||
|     @skip_copy_if_green |     @skip_copy_if_green | ||||||
|  | @ -341,7 +336,7 @@ class TestVersionDiscovery(unittest.TestCase): | ||||||
|         self.assertTrue(type(psycopg2.__libpq_version__) is int) |         self.assertTrue(type(psycopg2.__libpq_version__) is int) | ||||||
|         try: |         try: | ||||||
|             self.assertTrue(type(psycopg2.extensions.libpq_version()) is int) |             self.assertTrue(type(psycopg2.extensions.libpq_version()) is int) | ||||||
|         except NotSupportedError: |         except psycopg2.NotSupportedError: | ||||||
|             self.assertTrue(psycopg2.__libpq_version__ < 90100) |             self.assertTrue(psycopg2.__libpq_version__ < 90100) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user