mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-25 10:23:43 +03:00
Merge remote-tracking branch 'origin/master' into repl
This commit is contained in:
commit
862eda10c2
9
NEWS
9
NEWS
|
@ -6,6 +6,7 @@ What's new in psycopg 2.7
|
||||||
|
|
||||||
New features:
|
New features:
|
||||||
|
|
||||||
|
- Added `~psycopg2.extensions.parse_dsn()` function (:ticket:`#321`).
|
||||||
- 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
|
||||||
|
@ -15,6 +16,14 @@ New features:
|
||||||
(:ticket:`#326`).
|
(:ticket:`#326`).
|
||||||
|
|
||||||
|
|
||||||
|
What's new in psycopg 2.6.2
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- Report the server response status on errors (such as :ticket:`#281`).
|
||||||
|
- Raise NotSupportedError on unhandled server response status
|
||||||
|
(:ticket:`#352`).
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.6.1
|
What's new in psycopg 2.6.1
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,17 @@
|
||||||
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)
|
||||||
|
|
||||||
|
Parse connection string into a dictionary of keywords and values.
|
||||||
|
|
||||||
|
Uses libpq's ``PQconninfoParse`` to parse the string according to
|
||||||
|
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'}
|
||||||
|
|
||||||
.. class:: connection(dsn, async=False)
|
.. class:: connection(dsn, async=False)
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ The module interface respects the standard defined in the |DBAPI|_.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
|
- `~psycopg2.extensions.parse_dsn`
|
||||||
- libpq `connection string syntax`__
|
- libpq `connection string syntax`__
|
||||||
- libpq supported `connection parameters`__
|
- libpq supported `connection parameters`__
|
||||||
- libpq supported `environment variables`__
|
- libpq supported `environment variables`__
|
||||||
|
@ -91,7 +92,6 @@ The module interface respects the standard defined in the |DBAPI|_.
|
||||||
The parameters *connection_factory* and *async* are Psycopg extensions
|
The parameters *connection_factory* and *async* are Psycopg extensions
|
||||||
to the |DBAPI|.
|
to the |DBAPI|.
|
||||||
|
|
||||||
|
|
||||||
.. data:: apilevel
|
.. data:: apilevel
|
||||||
|
|
||||||
String constant stating the supported DB API level. For `psycopg2` is
|
String constant stating the supported DB API level. For `psycopg2` is
|
||||||
|
|
|
@ -56,11 +56,12 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, replicationMessage, lobject, Xid, libpq_version
|
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid, libpq_version, parse_dsn
|
||||||
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
|
||||||
|
|
||||||
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
|
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
|
||||||
|
from psycopg2._psycopg import replicationMessage
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from psycopg2._psycopg import set_wait_callback, get_wait_callback
|
from psycopg2._psycopg import set_wait_callback, get_wait_callback
|
||||||
|
|
|
@ -129,28 +129,33 @@ static int pthread_mutex_init(pthread_mutex_t *mutex, void* fake)
|
||||||
/* remove the inline keyword, since it doesn't work unless C++ file */
|
/* remove the inline keyword, since it doesn't work unless C++ file */
|
||||||
#define inline
|
#define inline
|
||||||
|
|
||||||
/* Hmmm, MSVC doesn't have a isnan/isinf function, but has _isnan function */
|
/* Hmmm, MSVC <2015 doesn't have a isnan/isinf function, but has _isnan function */
|
||||||
#if defined (_MSC_VER)
|
#if defined (_MSC_VER)
|
||||||
|
#if !defined(isnan)
|
||||||
#define isnan(x) (_isnan(x))
|
#define isnan(x) (_isnan(x))
|
||||||
/* The following line was hacked together from simliar code by Bjorn Reese
|
/* The following line was hacked together from simliar code by Bjorn Reese
|
||||||
* in libxml2 code */
|
* in libxml2 code */
|
||||||
#define isinf(x) ((_fpclass(x) == _FPCLASS_PINF) ? 1 \
|
#define isinf(x) ((_fpclass(x) == _FPCLASS_PINF) ? 1 \
|
||||||
: ((_fpclass(x) == _FPCLASS_NINF) ? -1 : 0))
|
: ((_fpclass(x) == _FPCLASS_NINF) ? -1 : 0))
|
||||||
|
#endif
|
||||||
#define strcasecmp(x, y) lstrcmpi(x, y)
|
#define strcasecmp(x, y) lstrcmpi(x, y)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* what's this, we have no round function either? */
|
||||||
#if (defined(__FreeBSD__) && __FreeBSD_version < 503000) \
|
#if (defined(__FreeBSD__) && __FreeBSD_version < 503000) \
|
||||||
|| (defined(_WIN32) && !defined(__GNUC__)) \
|
|| (defined(_WIN32) && !defined(__GNUC__)) \
|
||||||
|| (defined(sun) || defined(__sun__)) \
|
|| (defined(sun) || defined(__sun__)) \
|
||||||
&& (defined(__SunOS_5_8) || defined(__SunOS_5_9))
|
&& (defined(__SunOS_5_8) || defined(__SunOS_5_9))
|
||||||
/* what's this, we have no round function either? */
|
|
||||||
|
/* round has been added in the standard library with MSVC 2015 */
|
||||||
|
#if _MSC_VER < 1900
|
||||||
static double round(double num)
|
static double round(double num)
|
||||||
{
|
{
|
||||||
return (num >= 0) ? floor(num + 0.5) : ceil(num - 0.5);
|
return (num >= 0) ? floor(num + 0.5) : ceil(num - 0.5);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* resolve missing isinf() function for Solaris */
|
/* resolve missing isinf() function for Solaris */
|
||||||
#if defined (__SVR4) && defined (__sun)
|
#if defined (__SVR4) && defined (__sun)
|
||||||
|
|
|
@ -198,8 +198,10 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult **pgres)
|
||||||
raise and a meaningful message is better than an empty one.
|
raise and a meaningful message is better than an empty one.
|
||||||
Note: it can happen without it being our error: see ticket #82 */
|
Note: it can happen without it being our error: see ticket #82 */
|
||||||
if (err == NULL || err[0] == '\0') {
|
if (err == NULL || err[0] == '\0') {
|
||||||
PyErr_SetString(DatabaseError,
|
PyErr_Format(DatabaseError,
|
||||||
"error with no message from the libpq");
|
"error with status %s and no message from the libpq",
|
||||||
|
PQresStatus(pgres == NULL ?
|
||||||
|
PQstatus(conn->pgconn) : PQresultStatus(*pgres)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1897,11 +1899,26 @@ pq_fetch(cursorObject *curs, int no_result)
|
||||||
ex = -1;
|
ex = -1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case PGRES_BAD_RESPONSE:
|
||||||
Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn);
|
case PGRES_NONFATAL_ERROR:
|
||||||
|
case PGRES_FATAL_ERROR:
|
||||||
|
Dprintf("pq_fetch: uh-oh, something FAILED: status = %d pgconn = %p",
|
||||||
|
status, curs->conn);
|
||||||
pq_raise(curs->conn, curs, NULL);
|
pq_raise(curs->conn, curs, NULL);
|
||||||
ex = -1;
|
ex = -1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* PGRES_COPY_BOTH, PGRES_SINGLE_TUPLE, future statuses */
|
||||||
|
Dprintf("pq_fetch: got unsupported result: status = %d pgconn = %p",
|
||||||
|
status, curs->conn);
|
||||||
|
PyErr_Format(NotSupportedError,
|
||||||
|
"got server response with unsupported status %s",
|
||||||
|
PQresStatus(curs->pgres == NULL ?
|
||||||
|
PQstatus(curs->conn->pgconn) : PQresultStatus(curs->pgres)));
|
||||||
|
CLEARPGRES(curs->pgres);
|
||||||
|
ex = -1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* error checking, close the connection if necessary (some critical errors
|
/* error checking, close the connection if necessary (some critical errors
|
||||||
|
|
|
@ -113,6 +113,59 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define psyco_parse_dsn_doc "parse_dsn(dsn) -> dict"
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
char *err = NULL;
|
||||||
|
PQconninfoOption *options = NULL, *o;
|
||||||
|
PyObject *dict = NULL, *res = NULL, *dsn;
|
||||||
|
|
||||||
|
static char *kwlist[] = {"dsn", NULL};
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(dsn); /* for ensure_bytes */
|
||||||
|
if (!(dsn = psycopg_ensure_bytes(dsn))) { goto exit; }
|
||||||
|
|
||||||
|
options = PQconninfoParse(Bytes_AS_STRING(dsn), &err);
|
||||||
|
if (options == NULL) {
|
||||||
|
if (err != NULL) {
|
||||||
|
PyErr_Format(ProgrammingError, "error parsing the dsn: %s", err);
|
||||||
|
PQfreemem(err);
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(OperationalError, "PQconninfoParse() failed");
|
||||||
|
}
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(dict = PyDict_New())) { goto exit; }
|
||||||
|
for (o = options; o->keyword != NULL; o++) {
|
||||||
|
if (o->val != NULL) {
|
||||||
|
PyObject *value;
|
||||||
|
if (!(value = Text_FromUTF8(o->val))) { goto exit; }
|
||||||
|
if (PyDict_SetItemString(dict, o->keyword, value) != 0) {
|
||||||
|
Py_DECREF(value);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
Py_DECREF(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
res = dict;
|
||||||
|
dict = NULL;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
PQconninfoFree(options); /* safe on null */
|
||||||
|
Py_XDECREF(dict);
|
||||||
|
Py_XDECREF(dsn);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/** type registration **/
|
/** type registration **/
|
||||||
#define psyco_register_type_doc \
|
#define psyco_register_type_doc \
|
||||||
"register_type(obj, conn_or_curs) -> None -- register obj with psycopg type system\n\n" \
|
"register_type(obj, conn_or_curs) -> None -- register obj with psycopg type system\n\n" \
|
||||||
|
@ -709,6 +762,8 @@ error:
|
||||||
static PyMethodDef psycopgMethods[] = {
|
static PyMethodDef psycopgMethods[] = {
|
||||||
{"_connect", (PyCFunction)psyco_connect,
|
{"_connect", (PyCFunction)psyco_connect,
|
||||||
METH_VARARGS|METH_KEYWORDS, psyco_connect_doc},
|
METH_VARARGS|METH_KEYWORDS, psyco_connect_doc},
|
||||||
|
{"parse_dsn", (PyCFunction)psyco_parse_dsn,
|
||||||
|
METH_VARARGS|METH_KEYWORDS, psyco_parse_dsn_doc},
|
||||||
{"adapt", (PyCFunction)psyco_microprotocols_adapt,
|
{"adapt", (PyCFunction)psyco_microprotocols_adapt,
|
||||||
METH_VARARGS, psyco_microprotocols_adapt_doc},
|
METH_VARARGS, psyco_microprotocols_adapt_doc},
|
||||||
|
|
||||||
|
|
|
@ -23,16 +23,18 @@
|
||||||
# License for more details.
|
# License for more details.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.errorcodes
|
import psycopg2.errorcodes
|
||||||
import psycopg2.extensions
|
import 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
|
from testutils import skip_before_postgres, skip_after_postgres, skip_before_libpq
|
||||||
from testutils import ConnectingTestCase, skip_if_tpc_disabled
|
from testutils import ConnectingTestCase, skip_if_tpc_disabled
|
||||||
from testutils import skip_if_windows
|
from testutils import skip_if_windows
|
||||||
from testconfig import dsn, dbname
|
from testconfig import dsn, dbname
|
||||||
|
@ -152,7 +154,8 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
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(100, len(conn.notices))
|
self.assertEqual(len([n for n in conn.notices if 'CREATE TABLE' in n]),
|
||||||
|
100)
|
||||||
|
|
||||||
def test_notices_noappend(self):
|
def test_notices_noappend(self):
|
||||||
conn = self.conn
|
conn = self.conn
|
||||||
|
@ -307,6 +310,78 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
self.assert_('foobar' not in c.dsn, "password was not obscured")
|
self.assert_('foobar' not in c.dsn, "password was not obscured")
|
||||||
|
|
||||||
|
|
||||||
|
class ParseDsnTestCase(ConnectingTestCase):
|
||||||
|
def test_parse_dsn(self):
|
||||||
|
from psycopg2 import ProgrammingError
|
||||||
|
from psycopg2.extensions import parse_dsn
|
||||||
|
|
||||||
|
self.assertEqual(parse_dsn('dbname=test user=tester password=secret'),
|
||||||
|
dict(user='tester', password='secret', dbname='test'),
|
||||||
|
"simple DSN parsed")
|
||||||
|
|
||||||
|
self.assertRaises(ProgrammingError, parse_dsn,
|
||||||
|
"dbname=test 2 user=tester password=secret")
|
||||||
|
|
||||||
|
self.assertEqual(parse_dsn("dbname='test 2' user=tester password=secret"),
|
||||||
|
dict(user='tester', password='secret', dbname='test 2'),
|
||||||
|
"DSN with quoting parsed")
|
||||||
|
|
||||||
|
# Can't really use assertRaisesRegexp() here since we need to
|
||||||
|
# make sure that secret is *not* exposed in the error messgage
|
||||||
|
# (and it also requires python >= 2.7).
|
||||||
|
raised = False
|
||||||
|
try:
|
||||||
|
# unterminated quote after dbname:
|
||||||
|
parse_dsn("dbname='test 2 user=tester password=secret")
|
||||||
|
except ProgrammingError, e:
|
||||||
|
raised = True
|
||||||
|
self.assertTrue(str(e).find('secret') < 0,
|
||||||
|
"DSN was not exposed in error message")
|
||||||
|
except e:
|
||||||
|
self.fail("unexpected error condition: " + repr(e))
|
||||||
|
self.assertTrue(raised, "ProgrammingError raised due to invalid DSN")
|
||||||
|
|
||||||
|
@skip_before_libpq(9, 2)
|
||||||
|
def test_parse_dsn_uri(self):
|
||||||
|
from psycopg2.extensions import parse_dsn
|
||||||
|
|
||||||
|
self.assertEqual(parse_dsn('postgresql://tester:secret@/test'),
|
||||||
|
dict(user='tester', password='secret', dbname='test'),
|
||||||
|
"valid URI dsn parsed")
|
||||||
|
|
||||||
|
raised = False
|
||||||
|
try:
|
||||||
|
# extra '=' after port value
|
||||||
|
parse_dsn(dsn='postgresql://tester:secret@/test?port=1111=x')
|
||||||
|
except psycopg2.ProgrammingError, e:
|
||||||
|
raised = True
|
||||||
|
self.assertTrue(str(e).find('secret') < 0,
|
||||||
|
"URI was not exposed in error message")
|
||||||
|
except e:
|
||||||
|
self.fail("unexpected error condition: " + repr(e))
|
||||||
|
self.assertTrue(raised, "ProgrammingError raised due to invalid URI")
|
||||||
|
|
||||||
|
def test_unicode_value(self):
|
||||||
|
from psycopg2.extensions import parse_dsn
|
||||||
|
snowman = u"\u2603"
|
||||||
|
d = parse_dsn('dbname=' + snowman)
|
||||||
|
if sys.version_info[0] < 3:
|
||||||
|
self.assertEqual(d['dbname'], snowman.encode('utf8'))
|
||||||
|
else:
|
||||||
|
self.assertEqual(d['dbname'], snowman)
|
||||||
|
|
||||||
|
def test_unicode_key(self):
|
||||||
|
from psycopg2.extensions import parse_dsn
|
||||||
|
snowman = u"\u2603"
|
||||||
|
self.assertRaises(psycopg2.ProgrammingError, parse_dsn,
|
||||||
|
snowman + '=' + snowman)
|
||||||
|
|
||||||
|
def test_bad_param(self):
|
||||||
|
from psycopg2.extensions import parse_dsn
|
||||||
|
self.assertRaises(TypeError, parse_dsn, None)
|
||||||
|
self.assertRaises(TypeError, parse_dsn, 42)
|
||||||
|
|
||||||
|
|
||||||
class IsolationLevelsTestCase(ConnectingTestCase):
|
class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -1103,6 +1178,17 @@ class AutocommitTests(ConnectingTestCase):
|
||||||
self.assertEqual(cur.fetchone()[0], 'on')
|
self.assertEqual(cur.fetchone()[0], 'on')
|
||||||
|
|
||||||
|
|
||||||
|
class ReplicationTest(ConnectingTestCase):
|
||||||
|
@skip_before_postgres(9, 0)
|
||||||
|
def test_replication_not_supported(self):
|
||||||
|
conn = self.repl_connect()
|
||||||
|
if conn is None: return
|
||||||
|
cur = conn.cursor()
|
||||||
|
f = StringIO()
|
||||||
|
self.assertRaises(psycopg2.NotSupportedError,
|
||||||
|
cur.copy_expert, "START_REPLICATION 0/0", f)
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ dbhost = os.environ.get('PSYCOPG2_TESTDB_HOST', None)
|
||||||
dbport = os.environ.get('PSYCOPG2_TESTDB_PORT', None)
|
dbport = os.environ.get('PSYCOPG2_TESTDB_PORT', None)
|
||||||
dbuser = os.environ.get('PSYCOPG2_TESTDB_USER', None)
|
dbuser = os.environ.get('PSYCOPG2_TESTDB_USER', None)
|
||||||
dbpass = os.environ.get('PSYCOPG2_TESTDB_PASSWORD', None)
|
dbpass = os.environ.get('PSYCOPG2_TESTDB_PASSWORD', None)
|
||||||
|
repl_dsn = os.environ.get('PSYCOPG2_TEST_REPL_DSN',
|
||||||
|
"dbname=psycopg2_test replication=1")
|
||||||
|
|
||||||
# Check if we want to test psycopg's green path.
|
# Check if we want to test psycopg's green path.
|
||||||
green = os.environ.get('PSYCOPG2_TEST_GREEN', None)
|
green = os.environ.get('PSYCOPG2_TEST_GREEN', None)
|
||||||
|
|
|
@ -28,7 +28,7 @@ import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from testconfig import dsn
|
from testconfig import dsn, repl_dsn
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import unittest2
|
import unittest2
|
||||||
|
@ -103,11 +103,35 @@ class ConnectingTestCase(unittest.TestCase):
|
||||||
"%s (did you remember calling ConnectingTestCase.setUp()?)"
|
"%s (did you remember calling ConnectingTestCase.setUp()?)"
|
||||||
% e)
|
% e)
|
||||||
|
|
||||||
|
if 'dsn' in kwargs:
|
||||||
|
conninfo = kwargs.pop('dsn')
|
||||||
|
else:
|
||||||
|
conninfo = dsn
|
||||||
import psycopg2
|
import psycopg2
|
||||||
conn = psycopg2.connect(dsn, **kwargs)
|
conn = psycopg2.connect(conninfo, **kwargs)
|
||||||
self._conns.append(conn)
|
self._conns.append(conn)
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
|
def repl_connect(self, **kwargs):
|
||||||
|
"""Return a connection set up for replication
|
||||||
|
|
||||||
|
The connection is on "PSYCOPG2_TEST_REPL_DSN" unless overridden by
|
||||||
|
a *dsn* kwarg.
|
||||||
|
|
||||||
|
Should raise a skip test if not available, but guard for None on
|
||||||
|
old Python versions.
|
||||||
|
"""
|
||||||
|
if 'dsn' not in kwargs:
|
||||||
|
kwargs['dsn'] = repl_dsn
|
||||||
|
import psycopg2
|
||||||
|
try:
|
||||||
|
conn = self.connect(**kwargs)
|
||||||
|
except psycopg2.OperationalError, e:
|
||||||
|
return self.skipTest("replication db not configured: %s" % e)
|
||||||
|
|
||||||
|
conn.autocommit = True
|
||||||
|
return conn
|
||||||
|
|
||||||
def _get_conn(self):
|
def _get_conn(self):
|
||||||
if not hasattr(self, '_the_conn'):
|
if not hasattr(self, '_the_conn'):
|
||||||
self._the_conn = self.connect()
|
self._the_conn = self.connect()
|
||||||
|
@ -388,4 +412,3 @@ class py3_raises_typeerror(object):
|
||||||
if sys.version_info[0] >= 3:
|
if sys.version_info[0] >= 3:
|
||||||
assert type is TypeError
|
assert type is TypeError
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user