diff --git a/.travis.yml b/.travis.yml index e4fdfdae..a6f14172 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,10 @@ language: python -python: - - "2.6" - - "2.7" - - "pypy" -install: python setup.py install +before_install: + - sudo apt-get update -qq -y + - sudo apt-get install python3.4-dev + +install: + - pip install tox six + before_script: mysql -e 'create database mysqldb_test charset utf8;' -script: TESTDB=travis.cnf nosetests +script: TESTDB=travis.cnf tox diff --git a/MANIFEST.in b/MANIFEST.in index 54b1fd5a..4e03cc43 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,7 +6,6 @@ include HISTORY include INSTALL include README.md include GPL-2.0 -include pymemcompat.h include metadata.cfg include site.cfg include setup_common.py diff --git a/MySQLdb/__init__.py b/MySQLdb/__init__.py index 87616715..fc414810 100644 --- a/MySQLdb/__init__.py +++ b/MySQLdb/__init__.py @@ -73,7 +73,7 @@ def test_DBAPISet_set_inequality_membership(): assert FIELD_TYPE.DATE != STRING def Binary(x): - return str(x) + return bytes(x) def Connect(*args, **kwargs): """Factory function for connections.Connection.""" diff --git a/MySQLdb/compat.py b/MySQLdb/compat.py new file mode 100644 index 00000000..70580b61 --- /dev/null +++ b/MySQLdb/compat.py @@ -0,0 +1,12 @@ +import sys + +if sys.version_info[0] == 3: + PY2 = False + unicode = str + unichr = chr + long = int +else: + PY2 = True + unicode = unicode + unichr = unichr + long = long diff --git a/MySQLdb/connections.py b/MySQLdb/connections.py index 908706a3..cd803767 100644 --- a/MySQLdb/connections.py +++ b/MySQLdb/connections.py @@ -7,10 +7,11 @@ """ from MySQLdb import cursors +from MySQLdb.compat import unicode, PY2 from _mysql_exceptions import Warning, Error, InterfaceError, DataError, \ DatabaseError, OperationalError, IntegrityError, InternalError, \ NotSupportedError, ProgrammingError -import types, _mysql +import _mysql import re @@ -33,7 +34,11 @@ def defaulterrorhandler(connection, cursor, errorclass, errorvalue): connection.messages.append(error) del cursor del connection - raise errorclass, errorvalue + if errorclass is not None: + raise errorclass(errorvalue) + else: + raise Exception(errorvalue) + re_numeric_part = re.compile(r"^(\d+)") @@ -115,6 +120,7 @@ class object, used to create cursors (keyword only) columns are returned as strings. columns are returned as normal strings. Unicode objects will always be encoded to the connection's character set regardless of this setting. + Default to False on Python 2 and True on Python 3. charset If supplied, the connection character set will be changed @@ -170,7 +176,7 @@ class object, used to create cursors (keyword only) cursorclass = kwargs2.pop('cursorclass', self.default_cursor) charset = kwargs2.pop('charset', '') - if charset: + if charset or not PY2: use_unicode = True else: use_unicode = False @@ -199,13 +205,20 @@ class object, used to create cursors (keyword only) db = proxy(self) def _get_string_literal(): + # Note: string_literal() is called for bytes object on Python 3. def string_literal(obj, dummy=None): return db.string_literal(obj) return string_literal def _get_unicode_literal(): - def unicode_literal(u, dummy=None): - return db.literal(u.encode(unicode_literal.charset)) + if PY2: + # unicode_literal is called for only unicode object. + def unicode_literal(u, dummy=None): + return db.literal(u.encode(unicode_literal.charset)) + else: + # unicode_literal() is called for arbitrary object. + def unicode_literal(u, dummy=None): + return db.literal(str(u).encode(unicode_literal.charset)) return unicode_literal def _get_string_decoder(): @@ -229,8 +242,8 @@ def string_decoder(s): self.converter[FIELD_TYPE.VARCHAR].append((None, string_decoder)) self.converter[FIELD_TYPE.BLOB].append((None, string_decoder)) - self.encoders[types.StringType] = string_literal - self.encoders[types.UnicodeType] = unicode_literal + self.encoders[bytes] = string_literal + self.encoders[unicode] = unicode_literal self._transactional = self.server_capabilities & CLIENT.TRANSACTIONS if self._transactional: if autocommit is not None: @@ -275,7 +288,15 @@ def literal(self, o): applications. """ - return self.escape(o, self.encoders) + s = self.escape(o, self.encoders) + # Python 3 doesn't support % operation for bytes object. + # We should decode it before using %. + # Decoding with ascii and surrogateescape allows convert arbitrary + # bytes to unicode and back again. + # See http://python.org/dev/peps/pep-0383/ + if not PY2 and isinstance(s, bytes): + return s.decode('ascii', 'surrogateescape') + return s def begin(self): """Explicitly begin a connection. Non-standard. diff --git a/MySQLdb/converters.py b/MySQLdb/converters.py index 26c1f901..9937e250 100644 --- a/MySQLdb/converters.py +++ b/MySQLdb/converters.py @@ -35,16 +35,9 @@ from _mysql import string_literal, escape_sequence, escape_dict, escape, NULL from MySQLdb.constants import FIELD_TYPE, FLAG from MySQLdb.times import * +from MySQLdb.compat import PY2, long -try: - from types import IntType, LongType, FloatType, NoneType, TupleType, ListType, DictType, InstanceType, \ - StringType, UnicodeType, ObjectType, BooleanType, ClassType, TypeType -except ImportError: - # Python 3 - long = int - IntType, LongType, FloatType, NoneType = int, long, float, type(None) - TupleType, ListType, DictType, InstanceType = tuple, list, dict, None - StringType, UnicodeType, ObjectType, BooleanType = bytes, str, object, bool +NoneType = type(None) import array @@ -76,8 +69,6 @@ def Unicode2Str(s, d): is connection-dependent.""" return s.encode() -Long2Int = Thing2Str - def Float2Str(o, d): return '%.15g' % o @@ -86,43 +77,13 @@ def None2NULL(o, d): return NULL # duh def Thing2Literal(o, d): - """Convert something into a SQL string literal. If using MySQL-3.23 or newer, string_literal() is a method of the _mysql.MYSQL object, and this function will be overridden with that method when the connection is created.""" - return string_literal(o, d) -def Instance2Str(o, d): - - """ - - Convert an Instance to a string representation. If the __str__() - method produces acceptable output, then you don't need to add the - class to conversions; it will be handled by the default - converter. If the exact class is not found in d, it will use the - first class it can find for which o is an instance. - - """ - - if o.__class__ in d: - return d[o.__class__](o, d) - cl = filter(lambda x,o=o: - type(x) is ClassType - and isinstance(o, x), d.keys()) - if not cl: - cl = filter(lambda x,o=o: - type(x) is TypeType - and isinstance(o, x) - and d[x] is not Instance2Str, - d.keys()) - if not cl: - return d[StringType](o,d) - d[o.__class__] = d[cl[0]] - return d[cl[0]](o, d) - def char_array(s): return array.array('c', s) @@ -133,21 +94,19 @@ def quote_tuple(t, d): return "(%s)" % (','.join(escape_sequence(t, d))) conversions = { - IntType: Thing2Str, - LongType: Long2Int, - FloatType: Float2Str, + int: Thing2Str, + long: Thing2Str, + float: Float2Str, NoneType: None2NULL, - TupleType: quote_tuple, - ListType: quote_tuple, - DictType: escape_dict, - InstanceType: Instance2Str, + tuple: quote_tuple, + list: quote_tuple, + dict: escape_dict, ArrayType: array2Str, - StringType: Thing2Literal, # default - UnicodeType: Unicode2Str, - ObjectType: Instance2Str, - BooleanType: Bool2Str, + bool: Bool2Str, + Date: Thing2Literal, DateTimeType: DateTime2literal, DateTimeDeltaType: DateTimeDelta2literal, + str: Thing2Literal, # default set: Set2Str, FIELD_TYPE.TINY: int, FIELD_TYPE.SHORT: int, @@ -165,18 +124,22 @@ def quote_tuple(t, d): FIELD_TYPE.TIME: TimeDelta_or_None, FIELD_TYPE.DATE: Date_or_None, FIELD_TYPE.BLOB: [ - (FLAG.BINARY, str), + (FLAG.BINARY, bytes), ], FIELD_TYPE.STRING: [ - (FLAG.BINARY, str), + (FLAG.BINARY, bytes), ], FIELD_TYPE.VAR_STRING: [ - (FLAG.BINARY, str), + (FLAG.BINARY, bytes), ], FIELD_TYPE.VARCHAR: [ - (FLAG.BINARY, str), + (FLAG.BINARY, bytes), ], } +if PY2: + conversions[unicode] = Unicode2Str +else: + conversions[bytes] = Thing2Literal try: from decimal import Decimal @@ -184,6 +147,3 @@ def quote_tuple(t, d): conversions[FIELD_TYPE.NEWDECIMAL] = Decimal except ImportError: pass - - - diff --git a/MySQLdb/cursors.py b/MySQLdb/cursors.py index 348a586a..f241db2f 100644 --- a/MySQLdb/cursors.py +++ b/MySQLdb/cursors.py @@ -7,13 +7,9 @@ import re import sys -try: - from types import ListType, TupleType, UnicodeType -except ImportError: - # Python 3 - ListType = list - TupleType = tuple - UnicodeType = str +PY2 = sys.version_info[0] == 2 + +from MySQLdb.compat import unicode restr = r""" \s @@ -73,9 +69,9 @@ class BaseCursor(object): _defer_warnings = False def __init__(self, connection): - from weakref import proxy + from weakref import ref - self.connection = proxy(connection) + self.connection = ref(connection) self.description = None self.description_flags = None self.rowcount = -1 @@ -96,7 +92,8 @@ def __del__(self): def close(self): """Close the cursor. No further queries will be possible.""" - if not self.connection: return + if self.connection is None or self.connection() is None: + return while self.nextset(): pass self.connection = None @@ -157,9 +154,12 @@ def setoutputsizes(self, *args): """Does nothing, required by DB API.""" def _get_db(self): - if not self.connection: + con = self.connection + if con is not None: + con = con() + if con is None: self.errorhandler(self, ProgrammingError, "cursor closed") - return self.connection + return con def execute(self, query, args=None): @@ -177,18 +177,33 @@ def execute(self, query, args=None): """ del self.messages[:] db = self._get_db() - if isinstance(query, unicode): + + # NOTE: + # Python 2: query should be bytes when executing %. + # All unicode in args should be encoded to bytes on Python 2. + # Python 3: query should be str (unicode) when executing %. + # All bytes in args should be decoded with ascii and surrogateescape on Python 3. + # db.literal(obj) always returns str. + + if PY2 and isinstance(query, unicode): query = query.encode(db.unicode_literal.charset) + if args is not None: if isinstance(args, dict): - query = query % dict((key, db.literal(item)) - for key, item in args.iteritems()) + args = dict((key, db.literal(item)) for key, item in args.iteritems()) else: - query = query % tuple([db.literal(item) for item in args]) + args = tuple(map(db.literal, args)) + if not PY2 and isinstance(query, bytes): + query = query.decode(db.unicode_literal.charset) + query = query % args + + if isinstance(query, unicode): + query = query.encode(db.unicode_literal.charset, 'surrogateescape') + try: r = None r = self._query(query) - except TypeError, m: + except TypeError as m: if m.args[0] in ("not enough arguments for format string", "not all arguments converted"): self.messages.append((ProgrammingError, m.args[0])) @@ -228,8 +243,10 @@ def executemany(self, query, args): del self.messages[:] db = self._get_db() if not args: return - if isinstance(query, unicode): + if PY2 and isinstance(query, unicode): query = query.encode(db.unicode_literal.charset) + elif not PY2 and isinstance(query, bytes): + query = query.decode(db.unicode_literal.charset) m = insert_values.search(query) if not m: r = 0 @@ -247,7 +264,7 @@ def executemany(self, query, args): for key, item in a.iteritems())) else: q.append(qv % tuple([db.literal(item) for item in a])) - except TypeError, msg: + except TypeError as msg: if msg.args[0] in ("not enough arguments for format string", "not all arguments converted"): self.errorhandler(self, ProgrammingError, msg.args[0]) @@ -259,7 +276,10 @@ def executemany(self, query, args): exc, value, tb = sys.exc_info() del tb self.errorhandler(self, exc, value) - r = self._query('\n'.join([query[:p], ',\n'.join(q), query[e:]])) + qs = '\n'.join([query[:p], ',\n'.join(q), query[e:]]) + if not PY2: + qs = qs.encode(db.unicode_literal.charset, 'surrogateescape') + r = self._query(qs) if not self._defer_warnings: self._warning_check() return r @@ -305,7 +325,7 @@ def callproc(self, procname, args=()): q = "CALL %s(%s)" % (procname, ','.join(['@_%s_%d' % (procname, i) for i in range(len(args))])) - if type(q) is UnicodeType: + if isinstance(q, unicode): q = q.encode(db.unicode_literal.charset) self._query(q) self._executed = q diff --git a/MySQLdb/release.py b/MySQLdb/release.py deleted file mode 100644 index 5c30a6c2..00000000 --- a/MySQLdb/release.py +++ /dev/null @@ -1,4 +0,0 @@ - -__author__ = "Andy Dustman " -version_info = (1,2,4,'final',1) -__version__ = "1.2.4" diff --git a/README.md b/README.md index f0ca0321..c2331558 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ TODO ---- * A bugfix 1.2.4 release -* A 1.3.0 release that will support Python 2.7-3.3 +* A 1.3.0 release that will support Python 2.7-3.4 * The 2.0 version is being renamed [moist][] and is heavily refactored. Projects diff --git a/_mysql.c b/_mysql.c index 5b81c79d..a1554a50 100644 --- a/_mysql.c +++ b/_mysql.c @@ -32,11 +32,10 @@ PERFORMANCE OF THIS SOFTWARE. #define PyInt_FromLong(n) PyLong_FromLong(n) #define PyInt_Check(n) PyLong_Check(n) #define PyInt_AS_LONG(n) PyLong_AS_LONG(n) +#define PyString_FromString(s) PyUnicode_FromString(s) #endif -#if PY_VERSION_HEX > 0x02060000 + #include "bytesobject.h" -#endif -#include "pymemcompat.h" #include "structmember.h" #if defined(MS_WINDOWS) #include @@ -47,29 +46,12 @@ PERFORMANCE OF THIS SOFTWARE. #include "mysqld_error.h" #include "errmsg.h" -#if PY_VERSION_HEX < 0x02020000 -# define MyTuple_Resize(t,n,d) _PyTuple_Resize(t, n, d) -# define MyMember(a,b,c,d,e) {a,b,c,d} -# define MyMemberlist(x) struct memberlist x -# define MyAlloc(s,t) PyObject_New(s,&t) -# define MyFree(o) PyObject_Del(o) -#else -# define MyTuple_Resize(t,n,d) _PyTuple_Resize(t, n) -# define MyMember(a,b,c,d,e) {a,b,c,d,e} -# define MyMemberlist(x) struct PyMemberDef x -# define MyAlloc(s,t) (s *) t.tp_alloc(&t,0) +#define MyAlloc(s,t) (s *) t.tp_alloc(&t,0) #ifdef IS_PY3K -# define MyFree(o) PyObject_Del(o) +# define MyFree(o) Py_TYPE(o)->tp_free((PyObject*)o) #else # define MyFree(ob) ob->ob_type->tp_free((PyObject *)ob) #endif -#endif - -#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) -typedef int Py_ssize_t; -#define PY_SSIZE_T_MAX INT_MAX -#define PY_SSIZE_T_MIN INT_MIN -#endif static PyObject *_mysql_MySQLError; static PyObject *_mysql_Warning; @@ -134,11 +116,7 @@ _mysql_Exception(_mysql_ConnectionObject *c) if (!_mysql_server_init_done) { e = _mysql_InternalError; PyTuple_SET_ITEM(t, 0, PyInt_FromLong(-1L)); -#ifdef IS_PY3K - PyTuple_SET_ITEM(t, 1, PyUnicode_FromString("server not initialized")); -#else PyTuple_SET_ITEM(t, 1, PyString_FromString("server not initialized")); -#endif PyErr_SetObject(e, t); Py_DECREF(t); return NULL; @@ -148,11 +126,7 @@ _mysql_Exception(_mysql_ConnectionObject *c) e = _mysql_InterfaceError; else if (merr > CR_MAX_ERROR) { PyTuple_SET_ITEM(t, 0, PyInt_FromLong(-1L)); -#ifdef IS_PY3K - PyTuple_SET_ITEM(t, 1, PyUnicode_FromString("error totally whack")); -#else PyTuple_SET_ITEM(t, 1, PyString_FromString("error totally whack")); -#endif PyErr_SetObject(_mysql_InterfaceError, t); Py_DECREF(t); return NULL; @@ -240,11 +214,7 @@ _mysql_Exception(_mysql_ConnectionObject *c) break; } PyTuple_SET_ITEM(t, 0, PyInt_FromLong((long)merr)); -#ifdef IS_PY3K - PyTuple_SET_ITEM(t, 1, PyUnicode_FromString(mysql_error(&(c->connection)))); -#else PyTuple_SET_ITEM(t, 1, PyString_FromString(mysql_error(&(c->connection)))); -#endif PyErr_SetObject(e, t); Py_DECREF(t); return NULL; @@ -294,7 +264,7 @@ static PyObject *_mysql_server_init( for (i=0; i< cmd_argc; i++) { item = PySequence_GetItem(cmd_args, i); #ifdef IS_PY3K - s = PyUnicode_AS_DATA(item); + s = PyUnicode_AsUTF8(item); #else s = PyString_AsString(item); #endif @@ -324,7 +294,7 @@ static PyObject *_mysql_server_init( for (i=0; i< groupc; i++) { item = PySequence_GetItem(groups, i); #ifdef IS_PY3K - s = PyUnicode_AS_DATA(item); + s = PyUnicode_AsUTF8(item); #else s = PyString_AsString(item); #endif @@ -467,8 +437,12 @@ _mysql_ResultObject_Initialize( Py_INCREF(Py_None); } else if (PySequence_Check(fun)) { - int j, n2=PySequence_Size(fun); + long flags = fields[i].flags; PyObject *fun2=NULL; + int j, n2=PySequence_Size(fun); + if (fields[i].charsetnr != 63) { /* maaagic */ + flags &= ~BINARY_FLAG; + } for (j=0; j= 0x02020000 static int _mysql_ResultObject_traverse( _mysql_ResultObject *self, visitproc visit, @@ -530,7 +499,6 @@ static int _mysql_ResultObject_traverse( return visit(self->conn, arg); return 0; } -#endif static int _mysql_ResultObject_clear( _mysql_ResultObject *self) @@ -567,14 +535,14 @@ _mysql_ConnectionObject_Initialize( "client_flag", "ssl", "local_infile", #ifdef HAVE_MYSQL_OPT_TIMEOUTS - "read_timeout", - "write_timeout", + "read_timeout", + "write_timeout", #endif NULL } ; int connect_timeout = 0; #ifdef HAVE_MYSQL_OPT_TIMEOUTS - int read_timeout = 0; - int write_timeout = 0; + int read_timeout = 0; + int write_timeout = 0; #endif int compress = -1, named_pipe = -1, local_infile = -1; char *init_command=NULL, @@ -609,7 +577,7 @@ _mysql_ConnectionObject_Initialize( #ifdef IS_PY3K #define _stringsuck(d,t,s) {t=PyMapping_GetItemString(s,#d);\ - if(t){d=PyUnicode_AS_DATA(t);Py_DECREF(t);}\ + if(t){d=PyUnicode_AsUTF8(t);Py_DECREF(t);}\ PyErr_Clear();} #else #define _stringsuck(d,t,s) {t=PyMapping_GetItemString(s,#d);\ @@ -774,7 +742,6 @@ _mysql_connect( return (PyObject *) c; } -#if PY_VERSION_HEX >= 0x02020000 static int _mysql_ConnectionObject_traverse( _mysql_ConnectionObject *self, visitproc visit, @@ -784,7 +751,6 @@ static int _mysql_ConnectionObject_traverse( return visit(self->converter, arg); return 0; } -#endif static int _mysql_ConnectionObject_clear( _mysql_ConnectionObject *self) @@ -935,7 +901,7 @@ _mysql_ConnectionObject_commit( if (err) return _mysql_Exception(self); Py_INCREF(Py_None); return Py_None; -} +} static char _mysql_ConnectionObject_rollback__doc__[] = "Rolls backs the current transaction\n\ @@ -1031,11 +997,7 @@ _mysql_ConnectionObject_sqlstate( PyObject *args) { if (!PyArg_ParseTuple(args, "")) return NULL; -#ifdef IS_PY3K - return PyUnicode_FromString(mysql_sqlstate(&(self->connection))); -#else return PyString_FromString(mysql_sqlstate(&(self->connection))); -#endif } static char _mysql_ConnectionObject_warning_count__doc__[] = @@ -1084,11 +1046,7 @@ _mysql_ConnectionObject_error( { if (!PyArg_ParseTuple(args, "")) return NULL; check_connection(self); -#ifdef IS_PY3K - return PyUnicode_FromString(mysql_error(&(self->connection))); -#else return PyString_FromString(mysql_error(&(self->connection))); -#endif } static char _mysql_escape_string__doc__[] = @@ -1107,32 +1065,23 @@ _mysql_escape_string( PyObject *str; char *in, *out; int len, size; - if (!PyArg_ParseTuple(args, "s#:escape_string", &in, &size)) return NULL; -#ifdef IS_PY3K - str = PyUnicode_FromStringAndSize((char *) NULL, size*2+1); -#else - str = PyString_FromStringAndSize((char *) NULL, size*2+1); -#endif + if (!PyArg_ParseTuple(args, "y#:escape_string", &in, &size)) return NULL; + str = PyBytes_FromStringAndSize((char *) NULL, size*2+1); if (!str) return PyErr_NoMemory(); -#ifdef IS_PY3K - out = PyUnicode_AS_DATA(str); -#else - out = PyString_AS_STRING(str); -#endif + out = PyBytes_AS_STRING(str); #if MYSQL_VERSION_ID < 32321 len = mysql_escape_string(out, in, size); #else check_server_init(NULL); + + if (self && PyModule_Check((PyObject*)self)) + self = NULL; if (self && self->open) len = mysql_real_escape_string(&(self->connection), out, in, size); else len = mysql_escape_string(out, in, size); #endif -#ifdef IS_PY3K - if (PyUnicode_Resize(&str, len) < 0) return NULL; -#else - if (_PyString_Resize(&str, len) < 0) return NULL; -#endif + if (_PyBytes_Resize(&str, len) < 0) return NULL; return (str); } @@ -1154,22 +1103,32 @@ _mysql_string_literal( PyObject *str, *s, *o, *d; char *in, *out; int len, size; + if (self && PyModule_Check((PyObject*)self)) + self = NULL; if (!PyArg_ParseTuple(args, "O|O:string_literal", &o, &d)) return NULL; - s = PyObject_Str(o); - if (!s) return NULL; + if (PyBytes_Check(o)) { + s = o; + Py_INCREF(s); + } else { + s = PyObject_Str(o); + if (!s) return NULL; #ifdef IS_PY3K - in = PyUnicode_AS_DATA(s); - size = PyUnicode_GetSize(s); - str = PyUnicode_FromStringAndSize((char *) NULL, size*2+3); - if (!str) return PyErr_NoMemory(); - out = PyUnicode_AS_DATA(str); -#else - in = PyString_AsString(s); - size = PyString_GET_SIZE(s); - str = PyString_FromStringAndSize((char *) NULL, size*2+3); - if (!str) return PyErr_NoMemory(); - out = PyString_AS_STRING(str); + { + PyObject *t = PyUnicode_AsASCIIString(s); + Py_DECREF(s); + if (!t) return NULL; + s = t; + } #endif + } + in = PyBytes_AsString(s); + size = PyBytes_GET_SIZE(s); + str = PyBytes_FromStringAndSize((char *) NULL, size*2+3); + if (!str) { + Py_DECREF(s); + return PyErr_NoMemory(); + } + out = PyBytes_AS_STRING(str); #if MYSQL_VERSION_ID < 32321 len = mysql_escape_string(out+1, in, size); #else @@ -1180,11 +1139,7 @@ _mysql_string_literal( len = mysql_escape_string(out+1, in, size); #endif *out = *(out+len+1) = '\''; -#ifdef IS_PY3K - if (PyUnicode_Resize(&str, len+2) < 0) return NULL; -#else - if (_PyString_Resize(&str, len+2) < 0) return NULL; -#endif + if (_PyBytes_Resize(&str, len+2) < 0) return NULL; Py_DECREF(s); return (str); } @@ -1301,10 +1256,10 @@ _mysql_escape_dict( if (!PyArg_ParseTuple(args, "O!O:escape_dict", &PyDict_Type, &o, &d)) goto error; if (!PyMapping_Check(d)) { - PyErr_SetString(PyExc_TypeError, - "argument 2 must be a mapping"); - return NULL; - } + PyErr_SetString(PyExc_TypeError, + "argument 2 must be a mapping"); + return NULL; + } if (!(r = PyDict_New())) goto error; while (PyDict_Next(o, &ppos, &pkey, &item)) { quoted = _escape_item(item, d); @@ -1387,23 +1342,42 @@ static PyObject * _mysql_field_to_python( PyObject *converter, char *rowitem, - unsigned long length) + unsigned long length, + MYSQL_FIELD *field) { + int field_type = field->type; PyObject *v; +#ifdef IS_PY3K + // Return bytes for binary and string types. + int binary = 0; + if (field_type == FIELD_TYPE_TINY_BLOB || + field_type == FIELD_TYPE_MEDIUM_BLOB || + field_type == FIELD_TYPE_LONG_BLOB || + field_type == FIELD_TYPE_BLOB || + field_type == FIELD_TYPE_VAR_STRING || + field_type == FIELD_TYPE_STRING) { + binary = 1; + } +#endif if (rowitem) { - if (converter != Py_None) + if (converter != Py_None) { + const char *fmt = "s#"; v = PyObject_CallFunction(converter, +#ifdef IS_PY3K + binary ? "y#" : "s#", +#else "s#", +#endif rowitem, (int)length); - else + } else { #ifdef IS_PY3K - v = PyUnicode_FromStringAndSize(rowitem, - (int)length); -#else - v = PyString_FromStringAndSize(rowitem, - (int)length); + if (!binary) { + v = PyUnicode_FromStringAndSize(rowitem, (int)length); + } else #endif + v = PyBytes_FromStringAndSize(rowitem, (int)length); + } if (!v) return NULL; } else { @@ -1421,14 +1395,16 @@ _mysql_row_to_tuple( unsigned int n, i; unsigned long *length; PyObject *r, *c; + MYSQL_FIELD *fields; n = mysql_num_fields(self->result); if (!(r = PyTuple_New(n))) return NULL; length = mysql_fetch_lengths(self->result); + fields = mysql_fetch_fields(self->result); for (i=0; iconverter, i); - v = _mysql_field_to_python(c, row[i], length[i]); + v = _mysql_field_to_python(c, row[i], length[i], &fields[i]); if (!v) goto error; PyTuple_SET_ITEM(r, i, v); } @@ -1446,16 +1422,16 @@ _mysql_row_to_dict( unsigned int n, i; unsigned long *length; PyObject *r, *c; - MYSQL_FIELD *fields; + MYSQL_FIELD *fields; n = mysql_num_fields(self->result); if (!(r = PyDict_New())) return NULL; length = mysql_fetch_lengths(self->result); - fields = mysql_fetch_fields(self->result); + fields = mysql_fetch_fields(self->result); for (i=0; iconverter, i); - v = _mysql_field_to_python(c, row[i], length[i]); + v = _mysql_field_to_python(c, row[i], length[i], &fields[i]); if (!v) goto error; if (!PyMapping_HasKeyString(r, fields[i].name)) { PyMapping_SetItemString(r, fields[i].name, v); @@ -1490,11 +1466,11 @@ _mysql_row_to_dict_old( n = mysql_num_fields(self->result); if (!(r = PyDict_New())) return NULL; length = mysql_fetch_lengths(self->result); - fields = mysql_fetch_fields(self->result); + fields = mysql_fetch_fields(self->result); for (i=0; iconverter, i); - v = _mysql_field_to_python(c, row[i], length[i]); + v = _mysql_field_to_python(c, row[i], length[i], &fields[i]); if (!v) goto error; { int len=0; @@ -1543,7 +1519,7 @@ _mysql__fetch_row( goto error; } if (!row) { - if (MyTuple_Resize(r, i, 0) == -1) goto error; + if (_PyTuple_Resize(r, i) == -1) goto error; break; } v = convert_row(self, row); @@ -1606,7 +1582,7 @@ _mysql_ResultObject_fetch_row( if (rowsadded == -1) goto error; skiprows += rowsadded; if (rowsadded < maxrows) break; - if (MyTuple_Resize(&r, skiprows+maxrows, 0) == -1) + if (_PyTuple_Resize(&r, skiprows+maxrows) == -1) goto error; } } else { @@ -1684,11 +1660,7 @@ _mysql_ConnectionObject_character_set_name( #else s = "latin1"; #endif -#ifdef IS_PY3K - return PyUnicode_FromString(s); -#else return PyString_FromString(s); -#endif } #if MYSQL_VERSION_ID >= 50007 @@ -1749,18 +1721,6 @@ _mysql_ConnectionObject_get_character_set_info( check_connection(self); mysql_get_character_set_info(&(self->connection), &cs); if (!(result = PyDict_New())) return NULL; -#ifdef IS_PY3K - if (cs.csname) - PyDict_SetItemString(result, "name", PyUnicode_FromString(cs.csname)); - if (cs.name) - PyDict_SetItemString(result, "collation", PyUnicode_FromString(cs.name)); - if (cs.comment) - PyDict_SetItemString(result, "comment", PyUnicode_FromString(cs.comment)); - if (cs.dir) - PyDict_SetItemString(result, "dir", PyUnicode_FromString(cs.dir)); - PyDict_SetItemString(result, "mbminlen", PyInt_FromLong(cs.mbminlen)); - PyDict_SetItemString(result, "mbmaxlen", PyInt_FromLong(cs.mbmaxlen)); -#else if (cs.csname) PyDict_SetItemString(result, "name", PyString_FromString(cs.csname)); if (cs.name) @@ -1771,7 +1731,6 @@ _mysql_ConnectionObject_get_character_set_info( PyDict_SetItemString(result, "dir", PyString_FromString(cs.dir)); PyDict_SetItemString(result, "mbminlen", PyInt_FromLong(cs.mbminlen)); PyDict_SetItemString(result, "mbmaxlen", PyInt_FromLong(cs.mbmaxlen)); -#endif return result; } #endif @@ -1786,11 +1745,7 @@ _mysql_get_client_info( { if (!PyArg_ParseTuple(args, "")) return NULL; check_server_init(NULL); -#ifdef IS_PY3K - return PyUnicode_FromString(mysql_get_client_info()); -#else return PyString_FromString(mysql_get_client_info()); -#endif } static char _mysql_ConnectionObject_get_host_info__doc__[] = @@ -1805,11 +1760,7 @@ _mysql_ConnectionObject_get_host_info( { if (!PyArg_ParseTuple(args, "")) return NULL; check_connection(self); -#ifdef IS_PY3K - return PyUnicode_FromString(mysql_get_host_info(&(self->connection))); -#else return PyString_FromString(mysql_get_host_info(&(self->connection))); -#endif } static char _mysql_ConnectionObject_get_proto_info__doc__[] = @@ -1839,11 +1790,7 @@ _mysql_ConnectionObject_get_server_info( { if (!PyArg_ParseTuple(args, "")) return NULL; check_connection(self); -#ifdef IS_PY3K - return PyUnicode_FromString(mysql_get_server_info(&(self->connection))); -#else return PyString_FromString(mysql_get_server_info(&(self->connection))); -#endif } static char _mysql_ConnectionObject_info__doc__[] = @@ -1861,11 +1808,7 @@ _mysql_ConnectionObject_info( if (!PyArg_ParseTuple(args, "")) return NULL; check_connection(self); s = mysql_info(&(self->connection)); -#ifdef IS_PY3K - if (s) return PyUnicode_FromString(s); -#else if (s) return PyString_FromString(s); -#endif Py_INCREF(Py_None); return Py_None; } @@ -2108,12 +2051,7 @@ _mysql_ConnectionObject_stat( s = mysql_stat(&(self->connection)); Py_END_ALLOW_THREADS if (!s) return _mysql_Exception(self); -#ifdef IS_PY3K - return PyUnicode_FromString(s); -#else return PyString_FromString(s); -#endif - } static char _mysql_ConnectionObject_store_result__doc__[] = @@ -2239,11 +2177,7 @@ _mysql_ConnectionObject_repr( else sprintf(buf, "<_mysql.connection closed at %lx>", (long)self); -#ifdef IS_PY3K - return PyUnicode_FromString(buf); -#else return PyString_FromString(buf); -#endif } static char _mysql_ResultObject_data_seek__doc__[] = @@ -2317,13 +2251,8 @@ _mysql_ResultObject_repr( _mysql_ResultObject *self) { char buf[300]; - sprintf(buf, "<_mysql.result object at %lx>", - (long)self); -#ifdef IS_PY3K - return PyUnicode_FromString(buf); -#else + sprintf(buf, "<_mysql.result object at %lx>", (long)self); return PyString_FromString(buf); -#endif } static PyMethodDef _mysql_ConnectionObject_methods[] = { @@ -2547,42 +2476,42 @@ static PyMethodDef _mysql_ConnectionObject_methods[] = { {NULL, NULL} /* sentinel */ }; -static MyMemberlist(_mysql_ConnectionObject_memberlist)[] = { - MyMember( +static struct PyMemberDef _mysql_ConnectionObject_memberlist[] = { + { "open", T_INT, offsetof(_mysql_ConnectionObject,open), READONLY, "True if connection is open" - ), - MyMember( + }, + { "converter", T_OBJECT, offsetof(_mysql_ConnectionObject,converter), 0, "Type conversion mapping" - ), - MyMember( + }, + { "server_capabilities", T_UINT, offsetof(_mysql_ConnectionObject,connection.server_capabilities), READONLY, "Capabilites of server; consult MySQLdb.constants.CLIENT" - ), - MyMember( - "port", - T_UINT, - offsetof(_mysql_ConnectionObject,connection.port), - READONLY, - "TCP/IP port of the server connection" - ), - MyMember( - "client_flag", - T_UINT, - READONLY, - offsetof(_mysql_ConnectionObject,connection.client_flag), - "Client flags; refer to MySQLdb.constants.CLIENT" - ), + }, + { + "port", + T_UINT, + offsetof(_mysql_ConnectionObject,connection.port), + READONLY, + "TCP/IP port of the server connection" + }, + { + "client_flag", + T_UINT, + READONLY, + offsetof(_mysql_ConnectionObject,connection.client_flag), + "Client flags; refer to MySQLdb.constants.CLIENT" + }, {NULL} /* Sentinel */ }; @@ -2638,79 +2567,38 @@ static PyMethodDef _mysql_ResultObject_methods[] = { {NULL, NULL} /* sentinel */ }; -static MyMemberlist(_mysql_ResultObject_memberlist)[] = { - MyMember( +static struct PyMemberDef _mysql_ResultObject_memberlist[] = { + { "converter", T_OBJECT, offsetof(_mysql_ResultObject,converter), READONLY, "Type conversion mapping" - ), + }, {NULL} /* Sentinel */ }; static PyObject * -_mysql_ConnectionObject_getattr( +_mysql_ConnectionObject_getattro( _mysql_ConnectionObject *self, - char *name) + PyObject *name) { -#ifndef IS_PY3K - PyObject *res; - - res = Py_FindMethod(_mysql_ConnectionObject_methods, (PyObject *)self, name); - if (res != NULL) - return res; - PyErr_Clear(); -#endif - if (strcmp(name, "closed") == 0) - return PyInt_FromLong((long)!(self->open)); -#if PY_VERSION_HEX < 0x02020000 - return PyMember_Get((char *)self, _mysql_ConnectionObject_memberlist, name); + char *cname; +#ifdef IS_PY3K + cname = PyUnicode_AsUTF8(name); #else - { - MyMemberlist(*l); - for (l = _mysql_ConnectionObject_memberlist; l->name != NULL; l++) { - if (strcmp(l->name, name) == 0) - return PyMember_GetOne((char *)self, l); - } - PyErr_SetString(PyExc_AttributeError, name); - return NULL; - } + cname = PyString_AsString(name); #endif -} - -static PyObject * -_mysql_ResultObject_getattr( - _mysql_ResultObject *self, - char *name) -{ -#ifndef IS_PY3K - PyObject *res; + if (strcmp(cname, "closed") == 0) + return PyInt_FromLong((long)!(self->open)); - res = Py_FindMethod(_mysql_ResultObject_methods, (PyObject *)self, name); - if (res != NULL) - return res; - PyErr_Clear(); -#endif -#if PY_VERSION_HEX < 0x02020000 - return PyMember_Get((char *)self, _mysql_ResultObject_memberlist, name); -#else - { - MyMemberlist(*l); - for (l = _mysql_ResultObject_memberlist; l->name != NULL; l++) { - if (strcmp(l->name, name) == 0) - return PyMember_GetOne((char *)self, l); - } - PyErr_SetString(PyExc_AttributeError, name); - return NULL; - } -#endif + return PyObject_GenericGetAttr(self, name); } static int -_mysql_ConnectionObject_setattr( +_mysql_ConnectionObject_setattro( _mysql_ConnectionObject *self, - char *name, + PyObject *name, PyObject *v) { if (v == NULL) { @@ -2718,24 +2606,13 @@ _mysql_ConnectionObject_setattr( "can't delete connection attributes"); return -1; } -#if PY_VERSION_HEX < 0x02020000 - return PyMember_Set((char *)self, _mysql_ConnectionObject_memberlist, name, v); -#else - { - MyMemberlist(*l); - for (l = _mysql_ConnectionObject_memberlist; l->name != NULL; l++) - if (strcmp(l->name, name) == 0) - return PyMember_SetOne((char *)self, l, v); - } - PyErr_SetString(PyExc_AttributeError, name); - return -1; -#endif + return PyObject_GenericSetAttr(self, name, v); } static int -_mysql_ResultObject_setattr( +_mysql_ResultObject_setattro( _mysql_ResultObject *self, - char *name, + PyObject *name, PyObject *v) { if (v == NULL) { @@ -2743,18 +2620,7 @@ _mysql_ResultObject_setattr( "can't delete connection attributes"); return -1; } -#if PY_VERSION_HEX < 0x02020000 - return PyMember_Set((char *)self, _mysql_ResultObject_memberlist, name, v); -#else - { - MyMemberlist(*l); - for (l = _mysql_ResultObject_memberlist; l->name != NULL; l++) - if (strcmp(l->name, name) == 0) - return PyMember_SetOne((char *)self, l, v); - } - PyErr_SetString(PyExc_AttributeError, name); - return -1; -#endif + return PyObject_GenericSetAttr(self, name, v); } PyTypeObject _mysql_ConnectionObject_Type = { @@ -2769,8 +2635,8 @@ PyTypeObject _mysql_ConnectionObject_Type = { 0, (destructor)_mysql_ConnectionObject_dealloc, /* tp_dealloc */ 0, /*tp_print*/ - (getattrfunc)_mysql_ConnectionObject_getattr, /* tp_getattr */ - (setattrfunc)_mysql_ConnectionObject_setattr, /* tp_setattr */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ 0, /*tp_compare*/ (reprfunc)_mysql_ConnectionObject_repr, /* tp_repr */ @@ -2785,48 +2651,35 @@ PyTypeObject _mysql_ConnectionObject_Type = { 0, /* (hashfunc) tp_hash */ 0, /* (ternaryfunc) tp_call */ 0, /* (reprfunc) tp_str */ - 0, /* (getattrofunc) tp_getattro */ - 0, /* (setattrofunc) tp_setattro */ + (getattrofunc)_mysql_ConnectionObject_getattro, /* tp_getattro */ + (setattrofunc)_mysql_ConnectionObject_setattro, /* tp_setattro */ /* Functions to access object as input/output buffer */ 0, /* (PyBufferProcs *) tp_as_buffer */ - /* Flags to define presence of optional/expanded features */ -#if PY_VERSION_HEX < 0x02020000 - Py_TPFLAGS_DEFAULT, /* (long) tp_flags */ -#else + /* (tp_flags) Flags to define presence of optional/expanded features */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, -#endif _mysql_connect__doc__, /* (char *) tp_doc Documentation string */ -#if PY_VERSION_HEX >= 0x02000000 - /* Assigned meaning in release 2.0 */ -#if PY_VERSION_HEX >= 0x02020000 + /* call function for all accessible objects */ (traverseproc) _mysql_ConnectionObject_traverse, /* tp_traverse */ /* delete references to contained objects */ (inquiry) _mysql_ConnectionObject_clear, /* tp_clear */ -#else - /* not supporting pre-2.2 GC */ - 0, - 0, -#endif -#if PY_VERSION_HEX >= 0x02010000 - /* Assigned meaning in release 2.1 */ + /* rich comparisons */ 0, /* (richcmpfunc) tp_richcompare */ /* weak reference enabler */ 0, /* (long) tp_weaklistoffset */ -#if PY_VERSION_HEX >= 0x02020000 - /* Added in release 2.2 */ + /* Iterators */ 0, /* (getiterfunc) tp_iter */ 0, /* (iternextfunc) tp_iternext */ /* Attribute descriptor and subclassing stuff */ (struct PyMethodDef *)_mysql_ConnectionObject_methods, /* tp_methods */ - (MyMemberlist(*))_mysql_ConnectionObject_memberlist, /* tp_members */ + (struct PyMemberDef *)_mysql_ConnectionObject_memberlist, /* tp_members */ 0, /* (struct getsetlist *) tp_getset; */ 0, /* (struct _typeobject *) tp_base; */ 0, /* (PyObject *) tp_dict */ @@ -2840,9 +2693,6 @@ PyTypeObject _mysql_ConnectionObject_Type = { 0, /* (PyObject *) tp_bases */ 0, /* (PyObject *) tp_mro method resolution order */ 0, /* (PyObject *) tp_defined */ -#endif /* python 2.2 */ -#endif /* python 2.1 */ -#endif /* python 2.0 */ } ; PyTypeObject _mysql_ResultObject_Type = { @@ -2857,8 +2707,8 @@ PyTypeObject _mysql_ResultObject_Type = { 0, (destructor)_mysql_ResultObject_dealloc, /* tp_dealloc */ 0, /*tp_print*/ - (getattrfunc)_mysql_ResultObject_getattr, /* tp_getattr */ - (setattrfunc)_mysql_ResultObject_setattr, /* tp_setattr */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ 0, /*tp_compare*/ (reprfunc)_mysql_ResultObject_repr, /* tp_repr */ @@ -2873,49 +2723,36 @@ PyTypeObject _mysql_ResultObject_Type = { 0, /* (hashfunc) tp_hash */ 0, /* (ternaryfunc) tp_call */ 0, /* (reprfunc) tp_str */ - 0, /* (getattrofunc) tp_getattro */ - 0, /* (setattrofunc) tp_setattro */ + (getattrofunc)PyObject_GenericGetAttr, /* tp_getattro */ + (setattrofunc)_mysql_ResultObject_setattro, /* tp_setattr */ /* Functions to access object as input/output buffer */ 0, /* (PyBufferProcs *) tp_as_buffer */ /* Flags to define presence of optional/expanded features */ -#if PY_VERSION_HEX < 0x02020000 - Py_TPFLAGS_DEFAULT, /* (long) tp_flags */ -#else Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, -#endif _mysql_ResultObject__doc__, /* (char *) tp_doc Documentation string */ -#if PY_VERSION_HEX >= 0x02000000 - /* Assigned meaning in release 2.0 */ -#if PY_VERSION_HEX >= 0x02020000 + /* call function for all accessible objects */ (traverseproc) _mysql_ResultObject_traverse, /* tp_traverse */ /* delete references to contained objects */ (inquiry) _mysql_ResultObject_clear, /* tp_clear */ -#else - /* not supporting pre-2.2 GC */ - 0, - 0, -#endif -#if PY_VERSION_HEX >= 0x02010000 - /* Assigned meaning in release 2.1 */ + /* rich comparisons */ 0, /* (richcmpfunc) tp_richcompare */ /* weak reference enabler */ 0, /* (long) tp_weaklistoffset */ -#if PY_VERSION_HEX >= 0x02020000 - /* Added in release 2.2 */ + /* Iterators */ 0, /* (getiterfunc) tp_iter */ 0, /* (iternextfunc) tp_iternext */ /* Attribute descriptor and subclassing stuff */ (struct PyMethodDef *) _mysql_ResultObject_methods, /* tp_methods */ - (MyMemberlist(*)) _mysql_ResultObject_memberlist, /*tp_members */ + (struct PyMemberDef *) _mysql_ResultObject_memberlist, /*tp_members */ 0, /* (struct getsetlist *) tp_getset; */ 0, /* (struct _typeobject *) tp_base; */ 0, /* (PyObject *) tp_dict */ @@ -2929,9 +2766,6 @@ PyTypeObject _mysql_ResultObject_Type = { 0, /* (PyObject *) tp_bases */ 0, /* (PyObject *) tp_mro method resolution order */ 0, /* (PyObject *) tp_defined */ -#endif /* python 2.2 */ -#endif /* python 2.1 */ -#endif /* python 2.0 */ }; static PyMethodDef @@ -3059,32 +2893,31 @@ init_mysql(void) #endif { PyObject *dict, *module, *emod, *edict; -#ifdef IS_PY3K - module = PyModule_Create(&_mysqlmodule); - if (!module) return module; /* this really should never happen */ -#else - module = Py_InitModule4("_mysql", _mysql_methods, _mysql___doc__, - (PyObject *)NULL, PYTHON_API_VERSION); - if (!module) return; /* this really should never happen */ -#endif -#ifdef IS_PY3K - Py_TYPE(&_mysql_ConnectionObject_Type) = &PyType_Type; - Py_TYPE(&_mysql_ResultObject_Type) = &PyType_Type; -#else + +#ifndef IS_PY3K _mysql_ConnectionObject_Type.ob_type = &PyType_Type; _mysql_ResultObject_Type.ob_type = &PyType_Type; #endif #if PY_VERSION_HEX >= 0x02020000 _mysql_ConnectionObject_Type.tp_alloc = PyType_GenericAlloc; _mysql_ConnectionObject_Type.tp_new = PyType_GenericNew; -#ifndef IS_PY3K - _mysql_ConnectionObject_Type.tp_free = _PyObject_GC_Del; -#endif _mysql_ResultObject_Type.tp_alloc = PyType_GenericAlloc; _mysql_ResultObject_Type.tp_new = PyType_GenericNew; -#ifndef IS_PY3K - _mysql_ResultObject_Type.tp_free = _PyObject_GC_Del; #endif +#ifdef IS_PY3K + if (PyType_Ready(&_mysql_ConnectionObject_Type) < 0) + return NULL; + if (PyType_Ready(&_mysql_ResultObject_Type) < 0) + return NULL; + + module = PyModule_Create(&_mysqlmodule); + if (!module) return module; /* this really should never happen */ +#else + _mysql_ConnectionObject_Type.tp_free = _PyObject_GC_Del; + _mysql_ResultObject_Type.tp_free = _PyObject_GC_Del; + module = Py_InitModule4("_mysql", _mysql_methods, _mysql___doc__, + (PyObject *)NULL, PYTHON_API_VERSION); + if (!module) return; /* this really should never happen */ #endif if (!(dict = PyModule_GetDict(module))) goto error; @@ -3093,11 +2926,7 @@ init_mysql(void) dict, dict))) goto error; if (PyDict_SetItemString(dict, "__version__", -#ifdef IS_PY3K - PyUnicode_FromString(QUOTE(__version__)))) -#else PyString_FromString(QUOTE(__version__)))) -#endif goto error; if (PyDict_SetItemString(dict, "connection", (PyObject *)&_mysql_ConnectionObject_Type)) @@ -3146,13 +2975,8 @@ init_mysql(void) _mysql_NewException(dict, edict, "NotSupportedError"))) goto error; Py_DECREF(emod); -#ifdef IS_PY3K - if (!(_mysql_NULL = PyUnicode_FromString("NULL"))) - goto error; -#else if (!(_mysql_NULL = PyString_FromString("NULL"))) goto error; -#endif if (PyDict_SetItemString(dict, "NULL", _mysql_NULL)) goto error; error: if (PyErr_Occurred()) { @@ -3164,5 +2988,4 @@ init_mysql(void) return module; #endif } - - +/* vim: set ts=4 sts=4 sw=4 noexpandtab : */ diff --git a/metadata.cfg b/metadata.cfg index 49841c17..ea31c018 100644 --- a/metadata.cfg +++ b/metadata.cfg @@ -44,6 +44,7 @@ classifiers: Topic :: Database :: Database Engines/Servers py_modules: _mysql_exceptions + MySQLdb.compat MySQLdb.converters MySQLdb.connections MySQLdb.cursors diff --git a/pymemcompat.h b/pymemcompat.h deleted file mode 100644 index e7c538c4..00000000 --- a/pymemcompat.h +++ /dev/null @@ -1,87 +0,0 @@ - -/* The idea of this file is that you bundle it with your extension, - #include it, program to Python 2.3's memory API and have your - extension build with any version of Python from 1.5.2 through to - 2.3 (and hopefully beyond). */ - -#ifndef Py_PYMEMCOMPAT_H -#define Py_PYMEMCOMPAT_H - -#include "Python.h" - -/* There are three "families" of memory API: the "raw memory", "object - memory" and "object" families. (This is ignoring the matter of the - cycle collector, about which more is said below). - - Raw Memory: - - PyMem_Malloc, PyMem_Realloc, PyMem_Free - - Object Memory: - - PyObject_Malloc, PyObject_Realloc, PyObject_Free - - Object: - - PyObject_New, PyObject_NewVar, PyObject_Del - - The raw memory and object memory allocators both mimic the - malloc/realloc/free interface from ANSI C, but the object memory - allocator can (and, since 2.3, does by default) use a different - allocation strategy biased towards lots of lots of "small" - allocations. - - The object family is used for allocating Python objects, and the - initializers take care of some basic initialization (setting the - refcount to 1 and filling out the ob_type field) as well as having - a somewhat different interface. - - Do not mix the families! E.g. do not allocate memory with - PyMem_Malloc and free it with PyObject_Free. You may get away with - it quite a lot of the time, but there *are* scenarios where this - will break. You Have Been Warned. - - Also, in many versions of Python there are an insane amount of - memory interfaces to choose from. Use the ones described above. */ - -#if PY_VERSION_HEX < 0x01060000 -/* raw memory interface already present */ - -/* there is no object memory interface in 1.5.2 */ -#define PyObject_Malloc PyMem_Malloc -#define PyObject_Realloc PyMem_Realloc -#define PyObject_Free PyMem_Free - -/* the object interface is there, but the names have changed */ -#define PyObject_New PyObject_NEW -#define PyObject_NewVar PyObject_NEW_VAR -#define PyObject_Del PyMem_Free -#endif - -/* If your object is a container you probably want to support the - cycle collector, which was new in Python 2.0. - - Unfortunately, the interface to the collector that was present in - Python 2.0 and 2.1 proved to be tricky to use, and so changed in - 2.2 -- in a way that can't easily be papered over with macros. - - This file contains macros that let you program to the 2.2 GC API. - Your module will compile against any Python since version 1.5.2, - but the type will only participate in the GC in versions 2.2 and - up. Some work is still necessary on your part to only fill out the - tp_traverse and tp_clear fields when they exist and set tp_flags - appropriately. - - It is possible to support both the 2.0 and 2.2 GC APIs, but it's - not pretty and this comment block is too narrow to contain a - desciption of what's required... */ - -#if PY_VERSION_HEX < 0x020200B1 -#define PyObject_GC_New PyObject_New -#define PyObject_GC_NewVar PyObject_NewVar -#define PyObject_GC_Del PyObject_Del -#define PyObject_GC_Track(op) -#define PyObject_GC_UnTrack(op) -#endif - -#endif /* !Py_PYMEMCOMPAT_H */ diff --git a/setup_posix.py b/setup_posix.py index cfcf33ca..dc9b90bf 100644 --- a/setup_posix.py +++ b/setup_posix.py @@ -1,5 +1,9 @@ import os, sys -from ConfigParser import SafeConfigParser +try: + from ConfigParser import SafeConfigParser +except ImportError: + # Probably running Python 3.x + from configparser import ConfigParser as SafeConfigParser # This dequote() business is required for some older versions # of mysql_config diff --git a/tests/capabilities.py b/tests/capabilities.py index 076361cb..cbbe24fe 100644 --- a/tests/capabilities.py +++ b/tests/capabilities.py @@ -10,6 +10,8 @@ import unittest from configdb import connection_factory +from MySQLdb.compat import unichr + class DatabaseTest(unittest.TestCase): @@ -25,10 +27,8 @@ def setUp(self): db = connection_factory(**self.connect_kwargs) self.connection = db self.cursor = db.cursor() - # TODO: this needs to be re-evaluated for Python 3 - self.BLOBText = ''.join([chr(i) for i in range(256)] * 100); self.BLOBUText = u''.join([unichr(i) for i in range(16384)]) - self.BLOBBinary = self.db_module.Binary(''.join([chr(i) for i in range(256)] * 16)) + self.BLOBBinary = self.db_module.Binary((u''.join([unichr(i) for i in range(256)] * 16)).encode('latin1')) leak_test = True @@ -137,7 +137,7 @@ def test_truncation(self): columndefs = ( 'col1 INT', 'col2 VARCHAR(255)') def generator(row, col): if col == 0: return row - else: return ('%i' % (row%10))*((255-self.rows/2)+row) + else: return ('%i' % (row%10))*((255-self.rows//2)+row) self.create_table(columndefs) insert_statement = ('INSERT INTO %s VALUES (%s)' % (self.table, diff --git a/tests/dbapi20.py b/tests/dbapi20.py index ad292ae7..e1326d1b 100644 --- a/tests/dbapi20.py +++ b/tests/dbapi20.py @@ -177,8 +177,8 @@ def test_paramstyle(self): def test_Exceptions(self): # Make sure required exceptions exist, and are in the # defined heirarchy. - self.assertTrue(issubclass(self.driver.Warning,StandardError)) - self.assertTrue(issubclass(self.driver.Error,StandardError)) + self.assertTrue(issubclass(self.driver.Warning,Exception)) + self.assertTrue(issubclass(self.driver.Error,Exception)) self.assertTrue( issubclass(self.driver.InterfaceError,self.driver.Error) ) @@ -823,8 +823,8 @@ def test_Timestamp(self): # self.assertEqual(str(t1),str(t2)) def test_Binary(self): - b = self.driver.Binary('Something') - b = self.driver.Binary('') + b = self.driver.Binary(b'Something') + b = self.driver.Binary(b'') def test_STRING(self): self.assertTrue(hasattr(self.driver,'STRING'), diff --git a/tests/test_MySQLdb_capabilities.py b/tests/test_MySQLdb_capabilities.py index ead69822..c45353fd 100644 --- a/tests/test_MySQLdb_capabilities.py +++ b/tests/test_MySQLdb_capabilities.py @@ -76,8 +76,8 @@ def test_bug_2671682(self): from MySQLdb.constants import ER try: self.cursor.execute("describe some_non_existent_table"); - except self.connection.ProgrammingError, msg: - self.assertEquals(msg[0], ER.NO_SUCH_TABLE) + except self.connection.ProgrammingError as msg: + self.assertTrue(str(ER.NO_SUCH_TABLE) in str(msg)) def test_bug_3514287(self): c = self.cursor diff --git a/tox.ini b/tox.ini index e7bb9713..904c09c9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,8 @@ [tox] -envlist = py25, py26, py27, py33 +envlist = py26,py27, py33, py34 [testenv] -setenv = - TESTDB=travis.cnf commands = nosetests {posargs:-w tests -v} deps = - ipdb nose