connect() supports generic keyword arguments passed to the dsn

This commit is contained in:
Daniele Varrazzo 2011-11-17 01:51:25 +00:00
parent 4254fb8566
commit d2b67364fd
3 changed files with 97 additions and 162 deletions

View File

@ -20,7 +20,7 @@ The module interface respects the standard defined in the |DBAPI|_.
Create a new database session and return a new `connection` object.
You can specify the connection parameters either as a string::
The connection parameters can be specified either as a string::
conn = psycopg2.connect("dbname=test user=postgres password=secret")
@ -28,17 +28,23 @@ The module interface respects the standard defined in the |DBAPI|_.
conn = psycopg2.connect(database="test", user="postgres", password="secret")
The full list of available parameters is:
The basic connection parameters are:
- `!dbname` -- the database name (only in dsn string)
- `!database` -- the database name (only as keyword argument)
- `!user` -- user name used to authenticate
- `!password` -- password used to authenticate
- `!host` -- database host address (defaults to UNIX socket if not provided)
- `!port` -- connection port number (defaults to 5432 if not provided)
- `!sslmode` -- `SSL TCP/IP negotiation`__ mode
.. __: http://www.postgresql.org/docs/9.0/static/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS
Any other connection parameter supported by the client library/server can
be passed either in the connection string or as keyword. See the
PostgreSQL documentation for a complete `list of supported parameters`__.
Also note that the same parameters can be passed to the client library
using `environment variables`__.
.. __: http://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS
.. __: http://www.postgresql.org/docs/9.1/static/libpq-envars.html
Using the *connection_factory* parameter a different class or
connections factory can be specified. It should be a callable object
@ -48,6 +54,10 @@ The module interface respects the standard defined in the |DBAPI|_.
Using *async*\=1 an asynchronous connection will be created: see
:ref:`async-support` to know about advantages and limitations.
.. versionchanged:: 2.4.3
any keyword argument is passed to the connection. Previously only the
basic parameters (plus `!sslmode`) were supported as keywords.
.. extension::
The parameters *connection_factory* and *async* are Psycopg extensions

View File

@ -73,7 +73,7 @@ from psycopg2._psycopg import Error, Warning, DataError, DatabaseError, Programm
from psycopg2._psycopg import IntegrityError, InterfaceError, InternalError
from psycopg2._psycopg import NotSupportedError, OperationalError
from psycopg2._psycopg import connect, apilevel, threadsafety, paramstyle
from psycopg2._psycopg import _connect, apilevel, threadsafety, paramstyle
from psycopg2._psycopg import __version__
from psycopg2 import tz
@ -97,5 +97,64 @@ else:
_ext.register_adapter(Decimal, Adapter)
del Decimal, Adapter
def connect(dsn=None,
database=None, user=None, password=None, host=None, port=None,
connection_factory=None, async=False, **kwargs):
"""
Create a new database connection.
The connection parameters can be specified either as a string:
conn = psycopg2.connect("dbname=test user=postgres password=secret")
or using a set of keyword arguments:
conn = psycopg2.connect(database="test", user="postgres", password="secret")
The basic connection parameters are:
- *dbname*: the database name (only in dsn string)
- *database*: the database name (only as keyword argument)
- *user*: user name used to authenticate
- *password*: password used to authenticate
- *host*: database host address (defaults to UNIX socket if not provided)
- *port*: connection port number (defaults to 5432 if not provided)
Using the *connection_factory* parameter a different class or connections
factory can be specified. It should be a callable object taking a dsn
argument.
Using *async*=True an asynchronous connection will be created.
Any other keyword parameter will be passed to the underlying client
library: the list of supported parameter depends on the library version.
"""
if dsn is None:
items = []
if database is not None:
items.append(('dbname', database))
if user is not None:
items.append(('user', user))
if password is not None:
items.append(('password', password))
if host is not None:
items.append(('host', host))
if port is not None:
items.append(('port', port))
items.extend(
[(k, v) for (k, v) in kwargs.iteritems() if v is not None])
dsn = " ".join(["%s=%s" % item for item in items])
if not dsn:
raise InterfaceError('missing dsn and no parameters')
return _connect(dsn,
connection_factory=connection_factory, async=async)
__all__ = filter(lambda k: not k.startswith('_'), locals().keys())

View File

@ -75,177 +75,43 @@ HIDDEN PyObject *psyco_DescriptionType = NULL;
/** connect module-level function **/
#define psyco_connect_doc \
"connect(dsn, ...) -- Create a new database connection.\n\n" \
"This function supports two different but equivalent sets of arguments.\n" \
"A single data source name or ``dsn`` string can be used to specify the\n" \
"connection parameters, as follows::\n\n" \
" psycopg2.connect(\"dbname=xxx user=xxx ...\")\n\n" \
"If ``dsn`` is not provided it is possible to pass the parameters as\n" \
"keyword arguments; e.g.::\n\n" \
" psycopg2.connect(database='xxx', user='xxx', ...)\n\n" \
"The full list of available parameters is:\n\n" \
"- ``dbname`` -- database name (only in 'dsn')\n" \
"- ``database`` -- database name (only as keyword argument)\n" \
"- ``host`` -- host address (defaults to UNIX socket if not provided)\n" \
"- ``port`` -- port number (defaults to 5432 if not provided)\n" \
"- ``user`` -- user name used to authenticate\n" \
"- ``password`` -- password used to authenticate\n" \
"- ``sslmode`` -- SSL mode (see PostgreSQL documentation)\n\n" \
"- ``async`` -- if the connection should provide asynchronous API\n\n" \
"If the ``connection_factory`` keyword argument is not provided this\n" \
"function always return an instance of the `connection` class.\n" \
"Else the given sub-class of `extensions.connection` will be used to\n" \
"instantiate the connection object.\n\n" \
":return: New database connection\n" \
":rtype: `extensions.connection`"
static size_t
_psyco_connect_fill_dsn(char *dsn, const char *kw, const char *v, size_t i)
{
strcpy(&dsn[i], kw); i += strlen(kw);
strcpy(&dsn[i], v); i += strlen(v);
return i;
}
"_connect(dsn, [connection_factory], [async]) -- New database connection.\n\n"
static PyObject *
psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
{
PyObject *conn = NULL, *factory = NULL;
PyObject *pyport = NULL;
size_t idsn=-1;
int iport=-1;
const char *dsn_static = NULL;
char *dsn_dynamic=NULL;
const char *database=NULL, *user=NULL, *password=NULL;
const char *host=NULL, *sslmode=NULL;
char port[16];
PyObject *conn = NULL;
PyObject *factory = NULL;
const char *dsn = NULL;
int async = 0;
static char *kwlist[] = {"dsn", "database", "host", "port",
"user", "password", "sslmode",
"connection_factory", "async", NULL};
static char *kwlist[] = {"dsn", "connection_factory", "async", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|sssOsssOi", kwlist,
&dsn_static, &database, &host, &pyport,
&user, &password, &sslmode,
&factory, &async)) {
if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|Oi", kwlist,
&dsn, &factory, &async)) {
return NULL;
}
#if PY_MAJOR_VERSION < 3
if (pyport && PyString_Check(pyport)) {
PyObject *pyint = PyInt_FromString(PyString_AsString(pyport), NULL, 10);
if (!pyint) goto fail;
/* Must use PyInt_AsLong rather than PyInt_AS_LONG, because
* PyInt_FromString can return a PyLongObject: */
iport = PyInt_AsLong(pyint);
Py_DECREF(pyint);
if (iport == -1 && PyErr_Occurred())
goto fail;
}
else if (pyport && PyInt_Check(pyport)) {
iport = PyInt_AsLong(pyport);
if (iport == -1 && PyErr_Occurred())
goto fail;
}
#else
if (pyport && PyUnicode_Check(pyport)) {
PyObject *pyint = PyObject_CallFunction((PyObject*)&PyLong_Type,
"Oi", pyport, 10);
if (!pyint) goto fail;
iport = PyLong_AsLong(pyint);
Py_DECREF(pyint);
if (iport == -1 && PyErr_Occurred())
goto fail;
}
else if (pyport && PyLong_Check(pyport)) {
iport = PyLong_AsLong(pyport);
if (iport == -1 && PyErr_Occurred())
goto fail;
}
#endif
else if (pyport != NULL) {
PyErr_SetString(PyExc_TypeError, "port must be a string or int");
goto fail;
Dprintf("psyco_connect: dsn = '%s', async = %d", dsn, async);
/* allocate connection, fill with errors and return it */
if (factory == NULL || factory == Py_None) {
factory = (PyObject *)&connectionType;
}
if (iport > 0)
PyOS_snprintf(port, 16, "%d", iport);
if (dsn_static == NULL) {
size_t l = 46; /* len(" dbname= user= password= host= port= sslmode=\0") */
if (database) l += strlen(database);
if (host) l += strlen(host);
if (iport > 0) l += strlen(port);
if (user) l += strlen(user);
if (password) l += strlen(password);
if (sslmode) l += strlen(sslmode);
dsn_dynamic = malloc(l*sizeof(char));
if (dsn_dynamic == NULL) {
PyErr_SetString(InterfaceError, "dynamic dsn allocation failed");
goto fail;
}
idsn = 0;
if (database)
idsn = _psyco_connect_fill_dsn(dsn_dynamic, " dbname=", database, idsn);
if (host)
idsn = _psyco_connect_fill_dsn(dsn_dynamic, " host=", host, idsn);
if (iport > 0)
idsn = _psyco_connect_fill_dsn(dsn_dynamic, " port=", port, idsn);
if (user)
idsn = _psyco_connect_fill_dsn(dsn_dynamic, " user=", user, idsn);
if (password)
idsn = _psyco_connect_fill_dsn(dsn_dynamic, " password=", password, idsn);
if (sslmode)
idsn = _psyco_connect_fill_dsn(dsn_dynamic, " sslmode=", sslmode, idsn);
if (idsn > 0) {
dsn_dynamic[idsn] = '\0';
memmove(dsn_dynamic, &dsn_dynamic[1], idsn);
}
else {
PyErr_SetString(InterfaceError, "missing dsn and no parameters");
goto fail;
}
}
{
const char *dsn = (dsn_static != NULL ? dsn_static : dsn_dynamic);
Dprintf("psyco_connect: dsn = '%s', async = %d", dsn, async);
/* allocate connection, fill with errors and return it */
if (factory == NULL) factory = (PyObject *)&connectionType;
/* Here we are breaking the connection.__init__ interface defined
* by psycopg2. So, if not requiring an async conn, avoid passing
* the async parameter. */
/* TODO: would it be possible to avoid an additional parameter
* to the conn constructor? A subclass? (but it would require mixins
* to further subclass) Another dsn parameter (but is not really
* a connection parameter that can be configured) */
if (!async) {
/* Here we are breaking the connection.__init__ interface defined
* by psycopg2. So, if not requiring an async conn, avoid passing
* the async parameter. */
/* TODO: would it be possible to avoid an additional parameter
* to the conn constructor? A subclass? (but it would require mixins
* to further subclass) Another dsn parameter (but is not really
* a connection parameter that can be configured) */
if (!async) {
conn = PyObject_CallFunction(factory, "s", dsn);
} else {
} else {
conn = PyObject_CallFunction(factory, "si", dsn, async);
}
}
goto cleanup;
fail:
assert (PyErr_Occurred());
if (conn != NULL) {
Py_DECREF(conn);
conn = NULL;
}
/* Fall through to cleanup: */
cleanup:
if (dsn_dynamic != NULL) {
free(dsn_dynamic);
}
return conn;
}
@ -754,7 +620,7 @@ exit:
/** method table and module initialization **/
static PyMethodDef psycopgMethods[] = {
{"connect", (PyCFunction)psyco_connect,
{"_connect", (PyCFunction)psyco_connect,
METH_VARARGS|METH_KEYWORDS, psyco_connect_doc},
{"adapt", (PyCFunction)psyco_microprotocols_adapt,
METH_VARARGS, psyco_microprotocols_adapt_doc},