mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-25 10:23:43 +03:00
Move parse_dsn to extensions, add tests
This commit is contained in:
parent
6c57e4a648
commit
6a2f21aa14
|
@ -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,7 +78,7 @@ The module interface respects the standard defined in the |DBAPI|_.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
- `parse_dsn`
|
- `~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`__
|
||||||
|
@ -92,18 +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|.
|
||||||
|
|
||||||
.. 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.parse_dsn('dbname=test user=postgres password=secret')
|
|
||||||
{'password': 'secret', 'user': 'postgres', 'dbname': 'test'}
|
|
||||||
|
|
||||||
.. 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,7 +56,7 @@ from psycopg2._psycopg import Error, Warning, DataError, DatabaseError, Programm
|
||||||
from psycopg2._psycopg import IntegrityError, InterfaceError, InternalError
|
from psycopg2._psycopg import IntegrityError, InterfaceError, InternalError
|
||||||
from psycopg2._psycopg import NotSupportedError, OperationalError
|
from psycopg2._psycopg import NotSupportedError, OperationalError
|
||||||
|
|
||||||
from psycopg2._psycopg import _connect, parse_dsn, apilevel, threadsafety, paramstyle
|
from psycopg2._psycopg import _connect, apilevel, threadsafety, paramstyle
|
||||||
from psycopg2._psycopg import __version__
|
from psycopg2._psycopg import __version__
|
||||||
|
|
||||||
from psycopg2 import tz
|
from psycopg2 import tz
|
||||||
|
|
|
@ -56,7 +56,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid
|
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid, 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
|
||||||
|
|
||||||
|
|
|
@ -112,13 +112,12 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define psyco_parse_dsn_doc \
|
#define psyco_parse_dsn_doc "parse_dsn(dsn) -> dict"
|
||||||
"parse_dsn(dsn) -- Parse database connection string.\n\n"
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
psyco_parse_dsn(PyObject *self, PyObject *args)
|
psyco_parse_dsn(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
char *dsn, *err;
|
char *dsn, *err = NULL;
|
||||||
PQconninfoOption *options = NULL, *o;
|
PQconninfoOption *options = NULL, *o;
|
||||||
PyObject *res = NULL, *value;
|
PyObject *res = NULL, *value;
|
||||||
|
|
||||||
|
@ -127,20 +126,33 @@ psyco_parse_dsn(PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
options = PQconninfoParse(dsn, &err);
|
options = PQconninfoParse(dsn, &err);
|
||||||
if (!options) {
|
if (options == NULL) {
|
||||||
PyErr_Format(PyExc_RuntimeError, "PQconninfoParse: %s: %s", dsn, err);
|
if (err != NULL) {
|
||||||
PQfreemem(err);
|
PyErr_Format(ProgrammingError, "error parsing the dsn: %s", err);
|
||||||
|
PQfreemem(err);
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(OperationalError, "PQconninfoParse() failed");
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = PyDict_New();
|
res = PyDict_New();
|
||||||
for (o = options; o->keyword != NULL; o++) {
|
if (res != NULL) {
|
||||||
if (o->val != NULL) {
|
for (o = options; o->keyword != NULL; o++) {
|
||||||
value = PyString_FromString(o->val);
|
if (o->val != NULL) {
|
||||||
if (value == NULL || PyDict_SetItemString(res, o->keyword, value) != 0) {
|
value = Text_FromUTF8(o->val);
|
||||||
Py_DECREF(res);
|
if (value == NULL) {
|
||||||
res = NULL;
|
Py_DECREF(res);
|
||||||
break;
|
res = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (PyDict_SetItemString(res, o->keyword, value) != 0) {
|
||||||
|
Py_DECREF(value);
|
||||||
|
Py_DECREF(res);
|
||||||
|
res = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Py_DECREF(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,6 +270,37 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
self.assert_(c.closed, "connection failed so it must be closed")
|
self.assert_(c.closed, "connection failed so it must be closed")
|
||||||
self.assert_('foobar' not in c.dsn, "password was not obscured")
|
self.assert_('foobar' not in c.dsn, "password was not obscured")
|
||||||
|
|
||||||
|
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.assertEqual(parse_dsn("dbname='test 2' user=tester password=secret"),
|
||||||
|
dict(user='tester', password='secret', dbname='test 2'),
|
||||||
|
"DSN with quoting parsed")
|
||||||
|
|
||||||
|
self.assertEqual(parse_dsn('postgresql://tester:secret@/test'),
|
||||||
|
dict(user='tester', password='secret', dbname='test'),
|
||||||
|
"simple URI dsn 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(e.message.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")
|
||||||
|
|
||||||
|
|
||||||
class IsolationLevelsTestCase(ConnectingTestCase):
|
class IsolationLevelsTestCase(ConnectingTestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user