This commit is contained in:
Monty Taylor
2014-01-02 11:31:00 -06:00
10 changed files with 109 additions and 26 deletions

1
.gitignore vendored
View File

@ -7,5 +7,6 @@
*.zip *.zip
*.egg *.egg
*.egg-info/ *.egg-info/
.tox/
build/ build/
dist/ dist/

View File

@ -1,6 +1,5 @@
language: python language: python
python: python:
- "2.5"
- "2.6" - "2.6"
- "2.7" - "2.7"
- "pypy" - "pypy"

View File

@ -139,6 +139,11 @@ class Connection(_mysql.connection):
local_infile local_infile
integer, non-zero enables LOAD LOCAL INFILE; zero disables integer, non-zero enables LOAD LOCAL INFILE; zero disables
autocommit
If False (default), autocommit is disabled.
If True, autocommit is enabled.
If None, autocommit isn't set and server default is used.
There are a number of undocumented, non-standard methods. See the There are a number of undocumented, non-standard methods. See the
documentation for the MySQL C API for some hints on what they do. documentation for the MySQL C API for some hints on what they do.
@ -182,6 +187,9 @@ class Connection(_mysql.connection):
kwargs2['client_flag'] = client_flag kwargs2['client_flag'] = client_flag
# PEP-249 requires autocommit to be initially off
autocommit = kwargs2.pop('autocommit', False)
super(Connection, self).__init__(*args, **kwargs2) super(Connection, self).__init__(*args, **kwargs2)
self.cursorclass = cursorclass self.cursorclass = cursorclass
self.encoders = dict([ (k, v) for k, v in conv.items() self.encoders = dict([ (k, v) for k, v in conv.items()
@ -225,10 +233,15 @@ class Connection(_mysql.connection):
self.encoders[types.UnicodeType] = unicode_literal self.encoders[types.UnicodeType] = unicode_literal
self._transactional = self.server_capabilities & CLIENT.TRANSACTIONS self._transactional = self.server_capabilities & CLIENT.TRANSACTIONS
if self._transactional: if self._transactional:
# PEP-249 requires autocommit to be initially off if autocommit is not None:
self.autocommit(False) self.autocommit(autocommit)
self.messages = [] self.messages = []
def autocommit(self, on):
on = bool(on)
if self.get_autocommit() != on:
_mysql.connection.autocommit(self, on)
def cursor(self, cursorclass=None): def cursor(self, cursorclass=None):
""" """
@ -241,6 +254,8 @@ class Connection(_mysql.connection):
return (cursorclass or self.cursorclass)(self) return (cursorclass or self.cursorclass)(self)
def __enter__(self): def __enter__(self):
if self.get_autocommit():
self.query("BEGIN")
return self.cursor() return self.cursor()
def __exit__(self, exc, value, tb): def __exit__(self, exc, value, tb):

View File

@ -129,13 +129,16 @@ def char_array(s):
def array2Str(o, d): def array2Str(o, d):
return Thing2Literal(o.tostring(), d) return Thing2Literal(o.tostring(), d)
def quote_tuple(t, d):
return "(%s)" % (','.join(escape_sequence(t, d)))
conversions = { conversions = {
IntType: Thing2Str, IntType: Thing2Str,
LongType: Long2Int, LongType: Long2Int,
FloatType: Float2Str, FloatType: Float2Str,
NoneType: None2NULL, NoneType: None2NULL,
TupleType: escape_sequence, TupleType: quote_tuple,
ListType: escape_sequence, ListType: quote_tuple,
DictType: escape_dict, DictType: escape_dict,
InstanceType: Instance2Str, InstanceType: Instance2Str,
ArrayType: array2Str, ArrayType: array2Str,

View File

@ -26,7 +26,7 @@ restr = r"""
(?: (?:
(?:\( (?:\(
# ( - editor hightlighting helper # ( - editor hightlighting helper
[^)]* .*
\)) \))
| |
' '
@ -180,7 +180,11 @@ class BaseCursor(object):
if isinstance(query, unicode): if isinstance(query, unicode):
query = query.encode(db.unicode_literal.charset) query = query.encode(db.unicode_literal.charset)
if args is not None: if args is not None:
query = query % db.literal(args) if isinstance(args, dict):
query = query % dict((key, db.literal(item))
for key, item in args.iteritems())
else:
query = query % tuple([db.literal(item) for item in args])
try: try:
r = None r = None
r = self._query(query) r = self._query(query)
@ -236,7 +240,13 @@ class BaseCursor(object):
e = m.end(1) e = m.end(1)
qv = m.group(1) qv = m.group(1)
try: try:
q = [ qv % db.literal(a) for a in args ] q = []
for a in args:
if isinstance(a, dict):
q.append(qv % dict((key, db.literal(item))
for key, item in a.iteritems()))
else:
q.append(qv % tuple([db.literal(item) for item in a]))
except TypeError, msg: except TypeError, msg:
if msg.args[0] in ("not enough arguments for format string", if msg.args[0] in ("not enough arguments for format string",
"not all arguments converted"): "not all arguments converted"):

View File

@ -51,7 +51,12 @@ def DateTime_or_None(s):
try: try:
d, t = s.split(sep, 1) d, t = s.split(sep, 1)
return datetime(*[ int(x) for x in d.split('-')+t.split(':') ]) if '.' in t:
t, ms = t.split('.',1)
ms = ms.ljust(6, '0')
else:
ms = 0
return datetime(*[ int(x) for x in d.split('-')+t.split(':')+[ms] ])
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
raise raise
except: except:
@ -60,9 +65,14 @@ def DateTime_or_None(s):
def TimeDelta_or_None(s): def TimeDelta_or_None(s):
try: try:
h, m, s = s.split(':') h, m, s = s.split(':')
h, m, s = int(h), int(m), float(s) if '.' in s:
td = timedelta(hours=abs(h), minutes=m, seconds=int(s), s, ms = s.split('.')
microseconds=int(math.modf(s)[0] * 1000000)) ms = ms.ljust(6, '0')
else:
ms = 0
h, m, s, ms = int(h), int(m), int(s), int(ms)
td = timedelta(hours=abs(h), minutes=m, seconds=s,
microseconds=ms)
if h < 0: if h < 0:
return -td return -td
else: else:
@ -74,9 +84,14 @@ def TimeDelta_or_None(s):
def Time_or_None(s): def Time_or_None(s):
try: try:
h, m, s = s.split(':') h, m, s = s.split(':')
h, m, s = int(h), int(m), float(s) if '.' in s:
return time(hour=h, minute=m, second=int(s), s, ms = s.split('.')
microsecond=int(math.modf(s)[0] * 1000000)) ms = ms.ljust(6, '0')
else:
ms = 0
h, m, s, ms = int(h), int(m), int(s), int(ms)
return time(hour=h, minute=m, second=s,
microsecond=ms)
except ValueError: except ValueError:
return None return None

View File

@ -121,7 +121,7 @@ static int _mysql_server_init_done = 0;
/* According to https://dev.mysql.com/doc/refman/5.1/en/mysql-options.html /* According to https://dev.mysql.com/doc/refman/5.1/en/mysql-options.html
The MYSQL_OPT_READ_TIMEOUT apear in the version 5.1.12 */ The MYSQL_OPT_READ_TIMEOUT apear in the version 5.1.12 */
#if MYSQL_VERSION_ID > 50112 #if MYSQL_VERSION_ID > 50112
#define HAVE_MYSQL_OPT_READ_TIMEOUT 1 #define HAVE_MYSQL_OPT_TIMEOUTS 1
#endif #endif
PyObject * PyObject *
@ -566,13 +566,15 @@ _mysql_ConnectionObject_Initialize(
"read_default_file", "read_default_group", "read_default_file", "read_default_group",
"client_flag", "ssl", "client_flag", "ssl",
"local_infile", "local_infile",
#ifdef HAVE_MYSQL_OPT_READ_TIMEOUT #ifdef HAVE_MYSQL_OPT_TIMEOUTS
"read_timeout", "read_timeout",
"write_timeout",
#endif #endif
NULL } ; NULL } ;
int connect_timeout = 0; int connect_timeout = 0;
#ifdef HAVE_MYSQL_OPT_READ_TIMEOUT #ifdef HAVE_MYSQL_OPT_TIMEOUTS
int read_timeout = 0; int read_timeout = 0;
int write_timeout = 0;
#endif #endif
int compress = -1, named_pipe = -1, local_infile = -1; int compress = -1, named_pipe = -1, local_infile = -1;
char *init_command=NULL, char *init_command=NULL,
@ -584,8 +586,8 @@ _mysql_ConnectionObject_Initialize(
check_server_init(-1); check_server_init(-1);
if (!PyArg_ParseTupleAndKeywords(args, kwargs, if (!PyArg_ParseTupleAndKeywords(args, kwargs,
#ifdef HAVE_MYSQL_OPT_READ_TIMEOUT #ifdef HAVE_MYSQL_OPT_TIMEOUTS
"|ssssisOiiisssiOii:connect", "|ssssisOiiisssiOiii:connect",
#else #else
"|ssssisOiiisssiOi:connect", "|ssssisOiiisssiOi:connect",
#endif #endif
@ -598,8 +600,9 @@ _mysql_ConnectionObject_Initialize(
&read_default_group, &read_default_group,
&client_flag, &ssl, &client_flag, &ssl,
&local_infile &local_infile
#ifdef HAVE_MYSQL_OPT_READ_TIMEOUT #ifdef HAVE_MYSQL_OPT_TIMEOUTS
, &read_timeout , &read_timeout
, &write_timeout
#endif #endif
)) ))
return -1; return -1;
@ -636,12 +639,17 @@ _mysql_ConnectionObject_Initialize(
mysql_options(&(self->connection), MYSQL_OPT_CONNECT_TIMEOUT, mysql_options(&(self->connection), MYSQL_OPT_CONNECT_TIMEOUT,
(char *)&timeout); (char *)&timeout);
} }
#ifdef HAVE_MYSQL_OPT_READ_TIMEOUT #ifdef HAVE_MYSQL_OPT_TIMEOUTS
if (read_timeout) { if (read_timeout) {
unsigned int timeout = read_timeout; unsigned int timeout = read_timeout;
mysql_options(&(self->connection), MYSQL_OPT_READ_TIMEOUT, mysql_options(&(self->connection), MYSQL_OPT_READ_TIMEOUT,
(char *)&timeout); (char *)&timeout);
} }
if (write_timeout) {
unsigned int timeout = write_timeout;
mysql_options(&(self->connection), MYSQL_OPT_WRITE_TIMEOUT,
(char *)&timeout);
}
#endif #endif
if (compress != -1) { if (compress != -1) {
mysql_options(&(self->connection), MYSQL_OPT_COMPRESS, 0); mysql_options(&(self->connection), MYSQL_OPT_COMPRESS, 0);
@ -891,7 +899,21 @@ _mysql_ConnectionObject_autocommit(
if (err) return _mysql_Exception(self); if (err) return _mysql_Exception(self);
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
static char _mysql_ConnectionObject_get_autocommit__doc__[] =
"Get the autocommit mode. True when enable; False when disable.\n";
static PyObject *
_mysql_ConnectionObject_get_autocommit(
_mysql_ConnectionObject *self,
PyObject *args)
{
if (self->connection.server_status & SERVER_STATUS_AUTOCOMMIT) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
}
static char _mysql_ConnectionObject_commit__doc__[] = static char _mysql_ConnectionObject_commit__doc__[] =
"Commits the current transaction\n\ "Commits the current transaction\n\
@ -2317,6 +2339,12 @@ static PyMethodDef _mysql_ConnectionObject_methods[] = {
METH_VARARGS, METH_VARARGS,
_mysql_ConnectionObject_autocommit__doc__ _mysql_ConnectionObject_autocommit__doc__
}, },
{
"get_autocommit",
(PyCFunction)_mysql_ConnectionObject_get_autocommit,
METH_NOARGS,
_mysql_ConnectionObject_get_autocommit__doc__
},
{ {
"commit", "commit",
(PyCFunction)_mysql_ConnectionObject_commit, (PyCFunction)_mysql_ConnectionObject_commit,

View File

@ -71,8 +71,9 @@ def get_config():
if i.startswith(compiler_flag('I')) ] if i.startswith(compiler_flag('I')) ]
if static: if static:
extra_objects.append(os.path.join( extra_objects.append(os.path.join(library_dirs[0],'lib%s.a' % client))
library_dirs[0],'lib%s.a' % client)) if client in libraries:
libraries.remove(client)
name = "MySQL-python" name = "MySQL-python"
if enabled(options, 'embedded'): if enabled(options, 'embedded'):

View File

@ -77,7 +77,7 @@ class test_MySQLdb(capabilities.DatabaseTest):
try: try:
self.cursor.execute("describe some_non_existent_table"); self.cursor.execute("describe some_non_existent_table");
except self.connection.ProgrammingError, msg: except self.connection.ProgrammingError, msg:
self.assertTrue(msg[0] == ER.NO_SUCH_TABLE) self.assertEquals(msg[0], ER.NO_SUCH_TABLE)
def test_bug_3514287(self): def test_bug_3514287(self):
c = self.cursor c = self.cursor

11
tox.ini Normal file
View File

@ -0,0 +1,11 @@
[tox]
envlist = py25, py26, py27, py33
[testenv]
setenv =
TESTDB=travis.cnf
commands =
nosetests {posargs:-w tests -v}
deps =
ipdb
nose