Index: MySQLdb/ez_setup.py
===================================================================
--- MySQLdb/ez_setup.py	(revision 633)
+++ MySQLdb/ez_setup.py	(working copy)
@@ -62,10 +62,10 @@
     if egg_name in md5_data:
         digest = md5(data).hexdigest()
         if digest != md5_data[egg_name]:
-            print >>sys.stderr, (
+            print((
                 "md5 validation of %s failed!  (Possible download problem?)"
                 % egg_name
-            )
+            ), file=sys.stderr)
             sys.exit(2)
     return data
 
@@ -95,14 +95,14 @@
         return do_download()       
     try:
         pkg_resources.require("setuptools>="+version); return
-    except pkg_resources.VersionConflict, e:
+    except pkg_resources.VersionConflict as e:
         if was_imported:
-            print >>sys.stderr, (
+            print((
             "The required version of setuptools (>=%s) is not available, and\n"
             "can't be installed while this script is running. Please install\n"
             " a more recent version first, using 'easy_install -U setuptools'."
             "\n\n(Currently using %r)"
-            ) % (version, e.args[0])
+            ) % (version, e.args[0]), file=sys.stderr)
             sys.exit(2)
         else:
             del pkg_resources, sys.modules['pkg_resources']    # reload ok
@@ -121,7 +121,7 @@
     with a '/'). `to_dir` is the directory where the egg will be downloaded.
     `delay` is the number of seconds to pause before an actual download attempt.
     """
-    import urllib2, shutil
+    import urllib.request, urllib.error, urllib.parse, shutil
     egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
     url = download_base + egg_name
     saveto = os.path.join(to_dir, egg_name)
@@ -147,7 +147,7 @@
                     version, download_base, delay, url
                 ); from time import sleep; sleep(delay)
             log.warn("Downloading %s", url)
-            src = urllib2.urlopen(url)
+            src = urllib.request.urlopen(url)
             # Read/write all in one block, so we don't create a corrupt file
             # if the download is interrupted.
             data = _validate_md5(egg_name, src.read())
@@ -208,10 +208,10 @@
                 os.unlink(egg)
     else:
         if setuptools.__version__ == '0.0.1':
-            print >>sys.stderr, (
+            print((
             "You have an obsolete version of setuptools installed.  Please\n"
             "remove it from your system entirely before rerunning this script."
-            )
+            ), file=sys.stderr)
             sys.exit(2)
 
     req = "setuptools>="+version
@@ -230,8 +230,8 @@
             from setuptools.command.easy_install import main
             main(argv)
         else:
-            print "Setuptools version",version,"or greater has been installed."
-            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+            print("Setuptools version",version,"or greater has been installed.")
+            print('(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)')
 
 def update_md5(filenames):
     """Update our built-in md5 registry"""
@@ -244,7 +244,7 @@
         md5_data[base] = md5(f.read()).hexdigest()
         f.close()
 
-    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data = ["    %r: %r,\n" % it for it in list(md5_data.items())]
     data.sort()
     repl = "".join(data)
 
@@ -254,7 +254,7 @@
 
     match = re.search("\nmd5_data = {\n([^}]+)}", src)
     if not match:
-        print >>sys.stderr, "Internal error!"
+        print("Internal error!", file=sys.stderr)
         sys.exit(2)
 
     src = src[:match.start(1)] + repl + src[match.end(1):]
Index: MySQLdb/src/results.c
===================================================================
--- MySQLdb/src/results.c	(revision 633)
+++ MySQLdb/src/results.c	(working copy)
@@ -184,7 +184,7 @@
 	if (!(d = PyTuple_New(n))) return NULL;
 	for (i=0; i<n; i++) {
 		PyObject *f;
-		if (!(f = PyInt_FromLong((long)fields[i].flags))) goto error;
+		if (!(f = PyLong_FromLong((long)fields[i].flags))) goto error;
 		PyTuple_SET_ITEM(d, i, f);
 	}
 	return d;
@@ -203,12 +203,12 @@
 	if (rowitem) {
 		if (decoder != Py_None)
 			v = PyObject_CallFunction(decoder,
-						  "s#",
+						  "y#", /* convert from raw bytes to a python "bytes" object */
 						  rowitem,
 						  (int)length);
 		else
-			v = PyString_FromStringAndSize(rowitem,
-						       (int)length);
+			v = PyBytes_FromStringAndSize(rowitem,
+						      (int)length);
 		if (!v)
 			return NULL;
 	} else {
@@ -439,7 +439,7 @@
 	PyObject *unused)
 {
 	check_result_connection(self);
-	return PyInt_FromLong((long)mysql_num_fields(self->result));
+	return PyLong_FromLong((long)mysql_num_fields(self->result));
 }
 
 static char _mysql_ResultObject_num_rows__doc__[] =
@@ -511,7 +511,7 @@
 		return NULL;
 	}
 	r = mysql_row_tell(self->result);
-	return PyInt_FromLong(r-self->result->data->data);
+	return PyLong_FromLong(r - self->result->data->data);
 }
 
 static void
@@ -531,7 +531,7 @@
 	char buf[300];
 	sprintf(buf, "<_mysql.result object at %lx>",
 		(long)self);
-	return PyString_FromString(buf);
+	return PyUnicode_FromString(buf);
 }
 
 static PyMethodDef _mysql_ResultObject_methods[] = {
@@ -591,47 +591,25 @@
 		"connection",
 		T_OBJECT,
 		offsetof(_mysql_ConnectionObject, connection),
-		RO,
+		READONLY,
 		"Connection associated with result"
 	},
 	{
 		"decoders",
 		T_OBJECT,
 		offsetof(_mysql_ResultObject, decoders),
-		RO,
+		READONLY,
 		"Field decoders for result set"
 	},
 	{
 		"fields",
 		T_OBJECT,
 		offsetof(_mysql_ResultObject, fields),
-		RO,
+		READONLY,
 		"Field metadata for result set"
 	},	{NULL} /* Sentinel */
 };
 
-static PyObject *
-_mysql_ResultObject_getattr(
-	_mysql_ResultObject *self,
-	char *name)
-{
-	PyObject *res;
-	struct PyMemberDef *l;
-
-	res = Py_FindMethod(_mysql_ResultObject_methods, (PyObject *)self, name);
-	if (res != NULL)
-		return res;
-	PyErr_Clear();
-
-	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;
-}
-
 static int
 _mysql_ResultObject_setattr(
 	_mysql_ResultObject *self,
@@ -655,14 +633,13 @@
 }
 
 PyTypeObject _mysql_ResultObject_Type = {
-	PyObject_HEAD_INIT(NULL)
-	0,
+	PyVarObject_HEAD_INIT(NULL, 0)
 	"_mysql.result",
 	sizeof(_mysql_ResultObject),
 	0,
 	(destructor)_mysql_ResultObject_dealloc, /* tp_dealloc */
 	0, /*tp_print*/
-	(getattrfunc)_mysql_ResultObject_getattr, /* tp_getattr */
+	(getattrfunc)0, /* tp_getattr */
 	(setattrfunc)_mysql_ResultObject_setattr, /* tp_setattr */
 	0, /*tp_compare*/
 	(reprfunc)_mysql_ResultObject_repr, /* tp_repr */
@@ -678,7 +655,7 @@
 	0, /* (hashfunc) tp_hash */
 	0, /* (ternaryfunc) tp_call */
 	0, /* (reprfunc) tp_str */
-	0, /* (getattrofunc) tp_getattro */
+	PyObject_GenericGetAttr, /* (getattrofunc) tp_getattro */
 	0, /* (setattrofunc) tp_setattro */
 
 	/* Functions to access object as input/output buffer */
Index: MySQLdb/src/connections.c
===================================================================
--- MySQLdb/src/connections.c	(revision 633)
+++ MySQLdb/src/connections.c	(working copy)
@@ -50,7 +50,7 @@
 		return -1;
 
 #define _stringsuck(d,t,s) {t=PyMapping_GetItemString(s,#d);\
-        if(t){d=PyString_AsString(t);Py_DECREF(t);}\
+        if(t){d=PyBytes_AsString(t);Py_DECREF(t);}\
         PyErr_Clear();}
 	
 	if (ssl) {
@@ -227,11 +227,11 @@
         char *in, *out;
         int len, size;
         if (!PyArg_ParseTuple(args, "s#:escape_string", &in, &size)) return NULL;
-        str = PyString_FromStringAndSize((char *) NULL, size*2+1);
+        str = PyBytes_FromStringAndSize((char *) NULL, size*2+1);
         if (!str) return PyErr_NoMemory();
-        out = PyString_AS_STRING(str);
+        out = PyBytes_AS_STRING(str);
 	len = mysql_real_escape_string(&(self->connection), out, in, size);
-        if (_PyString_Resize(&str, len) < 0) return NULL;
+        if (_PyBytes_Resize(&str, len) < 0) return NULL;
         return (str);
 }
 
@@ -252,12 +252,12 @@
         char *in, *out;
         int len, size;
         if (!PyArg_ParseTuple(args, "s#:string_literal", &in, &size)) return NULL;
-        str = PyString_FromStringAndSize((char *) NULL, size*2+3);
+        str = PyBytes_FromStringAndSize((char *) NULL, size*2+3);
         if (!str) return PyErr_NoMemory();
-        out = PyString_AS_STRING(str);
+        out = PyBytes_AS_STRING(str);
         len = mysql_real_escape_string(&(self->connection), out+1, in, size);
         *out = *(out+len+1) = '\'';
-        if (_PyString_Resize(&str, len+2) < 0) return NULL;
+        if (_PyBytes_Resize(&str, len+2) < 0) return NULL;
         return (str);
 }
 
@@ -418,7 +418,7 @@
 #endif
 	Py_END_ALLOW_THREADS
 	if (err > 0) return _mysql_Exception(self);
-	return PyInt_FromLong(err);
+	return PyLong_FromLong(err);
 }
 
 #if MYSQL_VERSION_ID >= 40100
@@ -441,7 +441,7 @@
 	err = mysql_set_server_option(&(self->connection), flags);
 	Py_END_ALLOW_THREADS
 	if (err) return _mysql_Exception(self);
-	return PyInt_FromLong(err);
+	return PyLong_FromLong(err);
 }		
 
 static char _mysql_ConnectionObject_sqlstate__doc__[] =
@@ -461,7 +461,7 @@
 	_mysql_ConnectionObject *self,
 	PyObject *unused)
 {
-	return PyString_FromString(mysql_sqlstate(&(self->connection)));
+	return PyUnicode_FromString(mysql_sqlstate(&(self->connection)));
 }
 
 static char _mysql_ConnectionObject_warning_count__doc__[] =
@@ -475,7 +475,7 @@
 	_mysql_ConnectionObject *self,
 	PyObject *unused)
 {
-	return PyInt_FromLong(mysql_warning_count(&(self->connection)));
+	return PyLong_FromLong(mysql_warning_count(&(self->connection)));
 }
 
 #endif
@@ -492,7 +492,7 @@
 	PyObject *unused)
 {
 	check_connection(self);
-	return PyInt_FromLong((long)mysql_errno(&(self->connection)));
+	return PyLong_FromLong((long)mysql_errno(&(self->connection)));
 }
 
 static char _mysql_ConnectionObject_error__doc__[] =
@@ -507,7 +507,7 @@
 	PyObject *unused)
 {
 	check_connection(self);
-	return PyString_FromString(mysql_error(&(self->connection)));
+	return PyUnicode_FromString(mysql_error(&(self->connection)));
 }
 
 #if MYSQL_VERSION_ID >= 32303
@@ -570,7 +570,7 @@
 #else
 	s = "latin1";
 #endif
-	return PyString_FromString(s);
+	return PyUnicode_FromString(s);
 }
 
 #if MYSQL_VERSION_ID >= 50007
@@ -631,15 +631,15 @@
 	mysql_get_character_set_info(&(self->connection), &cs);
 	if (!(result = PyDict_New())) return NULL;
 	if (cs.csname)
-		PyDict_SetItemString(result, "name", PyString_FromString(cs.csname));
+		PyDict_SetItemString(result, "name", PyUnicode_FromString(cs.csname));
 	if (cs.name)
-		PyDict_SetItemString(result, "collation", PyString_FromString(cs.name));
+		PyDict_SetItemString(result, "collation", PyUnicode_FromString(cs.name));
 	if (cs.comment)
-		PyDict_SetItemString(result, "comment", PyString_FromString(cs.comment));
+		PyDict_SetItemString(result, "comment", PyUnicode_FromString(cs.comment));
 	if (cs.dir)
-		PyDict_SetItemString(result, "dir", PyString_FromString(cs.dir));
-	PyDict_SetItemString(result, "mbminlen", PyInt_FromLong(cs.mbminlen));
-	PyDict_SetItemString(result, "mbmaxlen", PyInt_FromLong(cs.mbmaxlen));
+		PyDict_SetItemString(result, "dir", PyUnicode_FromString(cs.dir));
+	PyDict_SetItemString(result, "mbminlen", PyLong_FromLong(cs.mbminlen));
+	PyDict_SetItemString(result, "mbmaxlen", PyLong_FromLong(cs.mbmaxlen));
 	return result;
 }
 #endif
@@ -655,7 +655,7 @@
 	PyObject *unused)
 {
 	check_connection(self);
-	return PyString_FromString(mysql_get_host_info(&(self->connection)));
+	return PyUnicode_FromString(mysql_get_host_info(&(self->connection)));
 }
 
 static char _mysql_ConnectionObject_get_proto_info__doc__[] =
@@ -669,7 +669,7 @@
 	PyObject *unused)
 {
 	check_connection(self);
-	return PyInt_FromLong((long)mysql_get_proto_info(&(self->connection)));
+	return PyLong_FromLong((long)mysql_get_proto_info(&(self->connection)));
 }
 
 static char _mysql_ConnectionObject_get_server_info__doc__[] =
@@ -683,7 +683,7 @@
 	PyObject *unused)
 {
 	check_connection(self);
-	return PyString_FromString(mysql_get_server_info(&(self->connection)));
+	return PyUnicode_FromString(mysql_get_server_info(&(self->connection)));
 }
 
 static char _mysql_ConnectionObject_info__doc__[] =
@@ -701,7 +701,7 @@
 
 	check_connection(self);
 	s = mysql_info(&(self->connection));
-	if (s) return PyString_FromString(s);
+	if (s) return PyUnicode_FromString(s);
 	Py_INCREF(Py_None);
 	return Py_None;
 }
@@ -775,9 +775,9 @@
 {
 	check_connection(self);
 #if MYSQL_VERSION_ID < 32224
-	return PyInt_FromLong((long)mysql_num_fields(&(self->connection)));
+	return PyLong_FromLong((long)mysql_num_fields(&(self->connection)));
 #else
-	return PyInt_FromLong((long)mysql_field_count(&(self->connection)));
+	return PyLong_FromLong((long)mysql_field_count(&(self->connection)));
 #endif
 }
 
@@ -914,7 +914,7 @@
 	s = mysql_stat(&(self->connection));
 	Py_END_ALLOW_THREADS
 	if (!s) return _mysql_Exception(self);
-	return PyString_FromString(s);
+	return PyUnicode_FromString(s);
 }
 
 static char _mysql_ConnectionObject_store_result__doc__[] =
@@ -974,7 +974,7 @@
 	Py_BEGIN_ALLOW_THREADS
 	pid = mysql_thread_id(&(self->connection));
 	Py_END_ALLOW_THREADS
-	return PyInt_FromLong((long)pid);
+	return PyLong_FromLong((long)pid);
 }
 
 static char _mysql_ConnectionObject_use_result__doc__[] =
@@ -1038,7 +1038,7 @@
 	else
 		sprintf(buf, "<_mysql.connection closed at %lx>",
 			(long)self);
-	return PyString_FromString(buf);
+	return PyUnicode_FromString(buf);
 }
 
 static PyMethodDef _mysql_ConnectionObject_methods[] = {
@@ -1255,7 +1255,7 @@
 		"open",
 		T_INT,
 		offsetof(_mysql_ConnectionObject, open),
-		RO,
+		READONLY,
 		"True if connection is open"
 	},
 	{
@@ -1269,20 +1269,20 @@
 		"server_capabilities",
 		T_UINT,
 		offsetof(_mysql_ConnectionObject, connection.server_capabilities),
-		RO,
+		READONLY,
 		"Capabilites of server; consult MySQLdb.constants.CLIENT"
 	},
 	{
 		 "port",
 		 T_UINT,
 		 offsetof(_mysql_ConnectionObject, connection.port),
-		 RO,
+		 READONLY,
 		 "TCP/IP port of the server connection"
 	},
 	{
 		 "client_flag",
 		 T_UINT,
-		 RO,
+		 READONLY,
 		 offsetof(_mysql_ConnectionObject, connection.client_flag),
 		 "Client flags; refer to MySQLdb.constants.CLIENT"
 	},
@@ -1290,27 +1290,20 @@
 };
 
 static PyObject *
-_mysql_ConnectionObject_getattr(
+_mysql_ConnectionObject_getattro(
 	_mysql_ConnectionObject *self,
-	char *name)
+	PyObject *name)
 {
-	PyObject *res;
-	struct PyMemberDef *l;
-
-	res = Py_FindMethod(_mysql_ConnectionObject_methods, (PyObject *)self, name);
-	if (res != NULL)
-		return res;
-	PyErr_Clear();
-	if (strcmp(name, "closed") == 0)
-		return PyInt_FromLong((long)!(self->open));
-
-	for (l = _mysql_ConnectionObject_memberlist; l->name != NULL; l++) {
-		if (strcmp(l->name, name) == 0)
-			return PyMember_GetOne((char *)self, l);
+	if (!PyUnicode_Check(name)){
+		PyErr_Format(PyExc_TypeError,
+			     "attribute name must be string, not '%.200s'",
+			     name->ob_type->tp_name);
+		return NULL;
 	}
+	if (PyUnicode_CompareWithASCIIString(name,"closed") == 0)
+		return PyLong_FromLong((long)!(self->open));
 
-	PyErr_SetString(PyExc_AttributeError, name);
-	return NULL;
+	return PyObject_GenericGetAttr((PyObject*)self, name);
 }
 
 static int
@@ -1336,14 +1329,13 @@
 }
 
 PyTypeObject _mysql_ConnectionObject_Type = {
-	PyObject_HEAD_INIT(NULL)
-	0,
+	PyVarObject_HEAD_INIT(NULL, 0)
 	"_mysql.connection", /* (char *)tp_name For printing */
 	sizeof(_mysql_ConnectionObject), /* tp_basicsize */
 	0,
 	(destructor)_mysql_ConnectionObject_dealloc, /* tp_dealloc */
 	0, /*tp_print*/
-	(getattrfunc)_mysql_ConnectionObject_getattr, /* tp_getattr */
+	(getattrfunc)0, /* tp_getattr */
 	(setattrfunc)_mysql_ConnectionObject_setattr, /* tp_setattr */
 	0, /*tp_compare*/
 	(reprfunc)_mysql_ConnectionObject_repr, /* tp_repr */
@@ -1359,7 +1351,7 @@
 	0, /* (hashfunc) tp_hash */
 	0, /* (ternaryfunc) tp_call */
 	0, /* (reprfunc) tp_str */
-	0, /* (getattrofunc) tp_getattro */
+	(getattrofunc) _mysql_ConnectionObject_getattro, /* tp_getattro */
 	0, /* (setattrofunc) tp_setattro */
 
 	/* Functions to access object as input/output buffer */
@@ -1403,3 +1395,4 @@
 	0, /* (PyObject *) tp_mro method resolution order */
 	0, /* (PyObject *) tp_defined */
 };
+
Index: MySQLdb/src/mysqlmod.c
===================================================================
--- MySQLdb/src/mysqlmod.c	(revision 633)
+++ MySQLdb/src/mysqlmod.c	(working copy)
@@ -26,8 +26,8 @@
 	if (!(t = PyTuple_New(2))) return NULL;
 	if (!_mysql_server_init_done) {
 		e = _mysql_InternalError;
-		PyTuple_SET_ITEM(t, 0, PyInt_FromLong(-1L));
-		PyTuple_SET_ITEM(t, 1, PyString_FromString("server not initialized"));
+		PyTuple_SET_ITEM(t, 0, PyLong_FromLong(-1L));
+		PyTuple_SET_ITEM(t, 1, PyUnicode_FromString("server not initialized"));
 		PyErr_SetObject(e, t);
 		Py_DECREF(t);
 		return NULL;
@@ -36,14 +36,14 @@
 	if (!merr)
 		e = _mysql_InterfaceError;
 	else if (merr > CR_MAX_ERROR) {
-		PyTuple_SET_ITEM(t, 0, PyInt_FromLong(-1L));
-		PyTuple_SET_ITEM(t, 1, PyString_FromString("error totally whack"));
+		PyTuple_SET_ITEM(t, 0, PyLong_FromLong(-1L));
+		PyTuple_SET_ITEM(t, 1, PyUnicode_FromString("error totally whack"));
 		PyErr_SetObject(_mysql_InterfaceError, t);
 		Py_DECREF(t);
 		return NULL;
 	}
 	else {
-		PyObject *py_merr = PyInt_FromLong(merr);
+		PyObject *py_merr = PyLong_FromLong(merr);
 		e = PyDict_GetItem(_mysql_error_map, py_merr);
 		Py_DECREF(py_merr);
 		if (!e) {
@@ -51,8 +51,8 @@
 			else e = _mysql_OperationalError;
 		}
 	}
-	PyTuple_SET_ITEM(t, 0, PyInt_FromLong((long)merr));
-	PyTuple_SET_ITEM(t, 1, PyString_FromString(mysql_error(&(c->connection))));
+	PyTuple_SET_ITEM(t, 0, PyLong_FromLong((long)merr));
+	PyTuple_SET_ITEM(t, 1, PyUnicode_FromString(mysql_error(&(c->connection))));
 	PyErr_SetObject(e, t);
 	Py_DECREF(t);
 	return NULL;
@@ -101,11 +101,11 @@
 		cmd_args_c = (char **) PyMem_Malloc(cmd_argc*sizeof(char *));
 		for (i=0; i< cmd_argc; i++) {
 			item = PySequence_GetItem(cmd_args, i);
-			s = PyString_AsString(item);
+			s = PyBytes_AsString(item);
 			Py_DECREF(item);
 			if (!s) {
 				PyErr_SetString(PyExc_TypeError,
-						"args must contain strings");
+						"args must contain bytes");
 				goto finish;
 			}
 			cmd_args_c[i] = s;
@@ -126,11 +126,11 @@
 		groups_c = (char **) PyMem_Malloc((1+groupc)*sizeof(char *));
 		for (i=0; i< groupc; i++) {
 			item = PySequence_GetItem(groups, i);
-			s = PyString_AsString(item);
+			s = PyBytes_AsString(item);
 			Py_DECREF(item);
 			if (!s) {
 				PyErr_SetString(PyExc_TypeError,
-						"groups must contain strings");
+						"groups must contain bytes");
 				goto finish;
 			}
 			groups_c[i] = s;
@@ -180,7 +180,7 @@
 	PyObject *unused) {
 
 	check_server_init(NULL);
-	return PyInt_FromLong((long)mysql_thread_safe());
+	return PyLong_FromLong((long)mysql_thread_safe());
 }
 #endif
 
@@ -236,7 +236,7 @@
 	if (!itemconv) {
 		PyErr_Clear();
 		itemconv = PyObject_GetItem(d,
-				 (PyObject *) &PyString_Type);
+				 (PyObject *) &PyBytes_Type);
 	}
 	if (!itemconv) {
 		PyErr_SetString(PyExc_TypeError,
@@ -329,7 +329,7 @@
 	PyObject *unused)
 {
 	check_server_init(NULL);
-	return PyString_FromString(mysql_get_client_info());
+	return PyUnicode_FromString(mysql_get_client_info());
 }
 
 extern PyTypeObject _mysql_ConnectionObject_Type;
@@ -409,19 +409,43 @@
 (as of 3.23) are NOT implemented.\n\
 ";
 
-PyMODINIT_FUNC
-init_mysql(void)
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef moduledef = {
+	PyModuleDef_HEAD_INIT,
+	"_mysql",/* m_name */
+	_mysql___doc__,/* m_doc */
+	0,/* m_size */
+	_mysql_methods,/* m_methods */
+	NULL,/* m_reload */
+	NULL,/* m_traverse */
+	NULL,/* m_clear */
+	NULL,/* m_free */
+};
+#define MOD_ERROR_VAL NULL
+#else
+#define MOD_ERROR_VAL
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+PyMODINIT_FUNC PyInit__mysql(void)
+#else
+PyMODINIT_FUNC init_mysql(void)
+#endif
 {
 	PyObject *dict, *module, *emod, *edict, *version_tuple;
 
+#if PY_MAJOR_VERSION >= 3
+	module = PyModule_Create(&moduledef);
+#else
 	module = Py_InitModule3("_mysql", _mysql_methods, _mysql___doc__);
+#endif
 	if (!module)
-		return; /* this really should never happen */
+		return MOD_ERROR_VAL; /* this really should never happen */
 
 	/* Populate final object settings */
-	_mysql_ConnectionObject_Type.ob_type = &PyType_Type;
-	_mysql_ResultObject_Type.ob_type = &PyType_Type;
-	_mysql_FieldObject_Type.ob_type = &PyType_Type;
+	Py_TYPE(&_mysql_ConnectionObject_Type) = &PyType_Type;
+	Py_TYPE(&_mysql_ResultObject_Type) = &PyType_Type;
+	Py_TYPE(&_mysql_FieldObject_Type) = &PyType_Type;
 	_mysql_ConnectionObject_Type.tp_alloc = PyType_GenericAlloc;
 	_mysql_ConnectionObject_Type.tp_new = PyType_GenericNew;
 	_mysql_ConnectionObject_Type.tp_free = _PyObject_GC_Del;
@@ -502,11 +526,16 @@
 		goto error;
 	Py_DECREF(emod);
 
+	#if PY_MAJOR_VERSION >= 3
+	return module;
+	#endif
+
   error:
 	if (PyErr_Occurred())
 		PyErr_SetString(PyExc_ImportError,
 				"_mysql: init failed");
-	return;
+	return NULL;
 }
 
 
+
Index: MySQLdb/src/fields.c
===================================================================
--- MySQLdb/src/fields.c	(revision 633)
+++ MySQLdb/src/fields.c	(working copy)
@@ -61,7 +61,7 @@
 _mysql_FieldObject_repr(
 	_mysql_FieldObject *self)
 {
-	return PyString_FromFormat("<_mysql.field object at %p>", self);
+	return PyUnicode_FromFormat("<_mysql.field object at %p>", self);
 }
 
 static PyMethodDef _mysql_FieldObject_methods[] = {
@@ -73,14 +73,14 @@
 		"result",
 		T_OBJECT,
 		offsetof(_mysql_FieldObject, result),
-		RO,
+		READONLY,
 		"Result set"
 	},
 	{
 		"name",
 		T_STRING,
 		offsetof(_mysql_FieldObject, field.name),
-		RO,
+		READONLY,
 		"The name of the field. If the field was given\n\
 an alias with an AS clause, the value of name is the alias."
 	},
@@ -88,14 +88,14 @@
 		"org_name",
 		T_STRING,
 		offsetof(_mysql_FieldObject, field.org_name),
-		RO,
+		READONLY,
 		"The name of the field. Aliases are ignored."
 	},
 	{
 		"table",
 		T_STRING,
 		offsetof(_mysql_FieldObject, field.table),
-		RO,
+		READONLY,
 		"The name of the table containing this field,\n\
 if it isn't a calculated field. For calculated fields,\n\
 the table value is an empty string. If the column is selected from a view,\n\
@@ -106,7 +106,7 @@
 		"org_table",
 		T_STRING,
 		offsetof(_mysql_FieldObject, field.org_table),
-		RO,
+		READONLY,
 		"The name of the table. Aliases are ignored.\n\
 If the column is selected from a view, org_table names the underlying table.\n"
 	},
@@ -114,7 +114,7 @@
 		"db",
 		T_STRING,
 		offsetof(_mysql_FieldObject, field.db),
-		RO,
+		READONLY,
 		"The name of the database that the field comes from.\n\
 If the field is a calculated field, db is an empty string."
 	},
@@ -122,14 +122,14 @@
 		"catalog",
 		T_STRING,
 		offsetof(_mysql_FieldObject, field.catalog),
-		RO,
+		READONLY,
 		"The catalog name. This value is always \"def\"."
 	},
 	{
 		"length",
 		T_ULONG,
 		offsetof(_mysql_FieldObject, field.length),
-		RO,
+		READONLY,
 		"The width of the field.\n\
 as specified in the table definition.\n"
 	},
@@ -137,7 +137,7 @@
 		"max_length",
 		T_ULONG,
 		offsetof(_mysql_FieldObject, field.max_length),
-		RO,
+		READONLY,
 		"The maximum width of the field for the result set\n\
 (the length of the longest field value for the rows actually in the\n\
 result set). If you use conn.store_result(), this contains the\n\
@@ -148,21 +148,21 @@
 		"decimals",
 		T_UINT,
 		offsetof(_mysql_FieldObject, field.decimals),
-		RO,
+		READONLY,
 		"The number of decimals for numeric fields.\n"
 	},
 	{
 		"charsetnr",
 		T_UINT,
 		offsetof(_mysql_FieldObject, field.charsetnr),
-		RO,
+		READONLY,
 		"The character set number for the field."
 	},
 	{
 		"flags",
 		T_UINT,
 		offsetof(_mysql_FieldObject, field.flags),
-		RO,
+		READONLY,
 		"Different bit-flags for the field.\n\
 The bits are enumerated in MySQLdb.constants.FLAG.\n\
 The flags value may have zero or more of these bits set.\n"
@@ -171,35 +171,13 @@
 		"type",
 		T_UINT,
 		offsetof(_mysql_FieldObject, field.type),
-		RO,
+		READONLY,
 		"The type of the field. The type values\n\
 are enumerated in MySQLdb.constants.FIELD_TYPE.\n"
 	},
 	{NULL} /* Sentinel */
 };
 
-static PyObject *
-_mysql_FieldObject_getattr(
-	_mysql_FieldObject *self,
-	char *name)
-{
-	PyObject *res;
-	struct PyMemberDef *l;
-
-	res = Py_FindMethod(_mysql_FieldObject_methods, (PyObject *)self, name);
-	if (res != NULL)
-		return res;
-	PyErr_Clear();
-
-	for (l = _mysql_FieldObject_memberlist; l->name != NULL; l++) {
-		if (strcmp(l->name, name) == 0)
-			return PyMember_GetOne((char *)self, l);
-	}
-
-	PyErr_SetString(PyExc_AttributeError, name);
-	return NULL;
-}
-
 static int
 _mysql_FieldObject_setattr(
 	_mysql_FieldObject *self,
@@ -224,14 +202,13 @@
 }
 
 PyTypeObject _mysql_FieldObject_Type = {
-	PyObject_HEAD_INIT(NULL)
-	0,
+	PyVarObject_HEAD_INIT(NULL, 0)
 	"_mysql.field",
 	sizeof(_mysql_FieldObject),
 	0,
 	(destructor)_mysql_FieldObject_dealloc, /* tp_dealloc */
 	0, /*tp_print*/
-	(getattrfunc)_mysql_FieldObject_getattr, /* tp_getattr */
+	(getattrfunc)0, /* tp_getattr */
 	(setattrfunc)_mysql_FieldObject_setattr, /* tp_setattr */
 	0, /*tp_compare*/
 	(reprfunc)_mysql_FieldObject_repr, /* tp_repr */
@@ -247,7 +224,7 @@
 	0, /* (hashfunc) tp_hash */
 	0, /* (ternaryfunc) tp_call */
 	0, /* (reprfunc) tp_str */
-	0, /* (getattrofunc) tp_getattro */
+	PyObject_GenericGetAttr, /* (getattrofunc) tp_getattro */
 	0, /* (setattrofunc) tp_setattro */
 
 	/* Functions to access object as input/output buffer */
@@ -291,3 +268,4 @@
 	0, /* (PyObject *) tp_mro method resolution order */
 	0, /* (PyObject *) tp_defined */
 };
+
Index: MySQLdb/src/mysqlmod.h
===================================================================
--- MySQLdb/src/mysqlmod.h	(revision 633)
+++ MySQLdb/src/mysqlmod.h	(working copy)
@@ -14,7 +14,7 @@
 #include "errmsg.h"
 
 #define MyAlloc(s,t) (s *) t.tp_alloc(&t,0)
-#define MyFree(ob) ob->ob_type->tp_free((PyObject *)ob)
+#define MyFree(ob) Py_TYPE(ob)->tp_free((PyObject *)ob)
 
 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
 typedef int Py_ssize_t;
Index: MySQLdb/setup_common.py
===================================================================
--- MySQLdb/setup_common.py	(revision 633)
+++ MySQLdb/setup_common.py	(working copy)
@@ -1,4 +1,4 @@
-from ConfigParser import SafeConfigParser
+from configparser import SafeConfigParser
 
 def get_metadata_and_options():
     config = SafeConfigParser()
@@ -7,8 +7,8 @@
     metadata = dict(config.items('metadata'))
     options = dict(config.items('options'))
 
-    metadata['py_modules'] = filter(None, metadata['py_modules'].split('\n'))
-    metadata['classifiers'] = filter(None, metadata['classifiers'].split('\n'))
+    metadata['py_modules'] = [_f for _f in metadata['py_modules'].split('\n') if _f]
+    metadata['classifiers'] = [_f for _f in metadata['classifiers'].split('\n') if _f]
 
     return metadata, options
 
Index: MySQLdb/setup_windows.py
===================================================================
--- MySQLdb/setup_windows.py	(revision 633)
+++ MySQLdb/setup_windows.py	(working copy)
@@ -1,11 +1,11 @@
 def get_config():
-    import os, sys, _winreg
+    import os, sys, winreg
     from setup_common import get_metadata_and_options, enabled, create_release_file
 
     metadata, options = get_metadata_and_options()
 
-    serverKey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, options['registry_key'])
-    mysql_root, dummy = _winreg.QueryValueEx(serverKey,'Location')
+    serverKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, options['registry_key'])
+    mysql_root, dummy = winreg.QueryValueEx(serverKey,'Location')
 
     extra_objects = []
     static = enabled(options, 'static')
@@ -43,5 +43,5 @@
     return metadata, ext_options
 
 if __name__ == "__main__":
-    print """You shouldn't be running this directly; it is used by setup.py."""
+    print("""You shouldn't be running this directly; it is used by setup.py.""")
     
Index: MySQLdb/tests/capabilities.py
===================================================================
--- MySQLdb/tests/capabilities.py	(revision 633)
+++ MySQLdb/tests/capabilities.py	(working copy)
@@ -25,7 +25,7 @@
         self.connection = db
         self.cursor = db.cursor()
         self.BLOBText = ''.join([chr(i) for i in range(256)] * 100);
-        self.BLOBUText = u''.join([unichr(i) for i in range(16834)])
+        self.BLOBUText = ''.join([chr(i) for i in range(16834)])
         self.BLOBBinary = self.db_module.Binary(''.join([chr(i) for i in range(256)] * 16))
 
     leak_test = True
@@ -85,14 +85,14 @@
         data = [ [ generator(i,j) for j in range(len(columndefs)) ]
                  for i in range(self.rows) ]
         if self.debug:
-            print data
+            print(data)
         self.cursor.executemany(insert_statement, data)
         self.connection.commit()
         # verify
         self.cursor.execute('select * from %s' % self.table)
         l = self.cursor.fetchall()
         if self.debug:
-            print l
+            print(l)
         self.assertEquals(len(l), self.rows)
         try:
             for i in range(self.rows):
@@ -148,7 +148,7 @@
         try:
             self.cursor.execute(insert_statement, (0, '0'*256))
         except Warning:
-            if self.debug: print self.cursor.messages
+            if self.debug: print(self.cursor.messages)
         except self.connection.DataError:
             pass
         else:
@@ -163,7 +163,7 @@
                     data.append(generator(i,j))
                 self.cursor.execute(insert_statement,tuple(data))
         except Warning:
-            if self.debug: print self.cursor.messages
+            if self.debug: print(self.cursor.messages)
         except self.connection.DataError:
             pass
         else:
@@ -176,7 +176,7 @@
                      for i in range(self.rows) ]
             self.cursor.executemany(insert_statement, data)
         except Warning:
-            if self.debug: print self.cursor.messages
+            if self.debug: print(self.cursor.messages)
         except self.connection.DataError:
             pass
         else:
Index: MySQLdb/tests/test_MySQLdb_dbapi20.py
===================================================================
--- MySQLdb/tests/test_MySQLdb_dbapi20.py	(revision 633)
+++ MySQLdb/tests/test_MySQLdb_dbapi20.py	(working copy)
@@ -202,4 +202,4 @@
     
 if __name__ == '__main__':
     unittest.main()
-    print '''"Huh-huh, he said 'unit'." -- Butthead'''
+    print('''"Huh-huh, he said 'unit'." -- Butthead''')
Index: MySQLdb/tests/dbapi20.py
===================================================================
--- MySQLdb/tests/dbapi20.py	(revision 633)
+++ MySQLdb/tests/dbapi20.py	(working copy)
@@ -177,8 +177,8 @@
     def test_Exceptions(self):
         # Make sure required exceptions exist, and are in the
         # defined heirarchy.
-        self.failUnless(issubclass(self.driver.Warning,StandardError))
-        self.failUnless(issubclass(self.driver.Error,StandardError))
+        self.failUnless(issubclass(self.driver.Warning,Exception))
+        self.failUnless(issubclass(self.driver.Error,Exception))
         self.failUnless(
             issubclass(self.driver.InterfaceError,self.driver.Error)
             )
@@ -706,7 +706,7 @@
             that returns two result sets, first the 
 	    number of rows in booze then "name from booze"
         '''
-        raise NotImplementedError,'Helper not implemented'
+        raise NotImplementedError('Helper not implemented')
         #sql="""
         #    create procedure deleteme as
         #    begin
@@ -718,7 +718,7 @@
 
     def help_nextset_tearDown(self,cur):
         'If cleaning up is needed after nextSetTest'
-        raise NotImplementedError,'Helper not implemented'
+        raise NotImplementedError('Helper not implemented')
         #cur.execute("drop procedure deleteme")
 
     def test_nextset(self):
@@ -751,7 +751,7 @@
             con.close()
 
     def test_nextset(self):
-        raise NotImplementedError,'Drivers need to override this test'
+        raise NotImplementedError('Drivers need to override this test')
 
     def test_arraysize(self):
         # Not much here - rest of the tests for this are in test_fetchmany
@@ -786,7 +786,7 @@
 
     def test_setoutputsize(self):
         # Real test for setoutputsize is driver dependant
-        raise NotImplementedError,'Driver need to override this test'
+        raise NotImplementedError('Driver need to override this test')
 
     def test_None(self):
         con = self._connect()
Index: MySQLdb/tests/test_MySQLdb_capabilities.py
===================================================================
--- MySQLdb/tests/test_MySQLdb_capabilities.py	(revision 633)
+++ MySQLdb/tests/test_MySQLdb_capabilities.py	(working copy)
@@ -78,32 +78,32 @@
         from MySQLdb.constants import ER
         try:
             self.cursor.execute("describe some_non_existent_table");
-        except self.connection.ProgrammingError, msg:
-            self.failUnless(msg[0] == ER.NO_SUCH_TABLE)
+        except self.connection.ProgrammingError as msg:
+            self.assertEqual(msg[0], ER.NO_SUCH_TABLE)
     
     def test_INSERT_VALUES(self):
         from MySQLdb.cursors import INSERT_VALUES
         query = """INSERT FOO (a, b, c) VALUES (%s, %s, %s)"""
         matched = INSERT_VALUES.match(query)
-        self.failUnless(matched)
+        self.assertTrue(matched)
         start = matched.group('start')
         end = matched.group('end')
         values = matched.group('values')
-        self.failUnless(start == """INSERT FOO (a, b, c) VALUES """)
-        self.failUnless(values == "(%s, %s, %s)")
-        self.failUnless(end == "")
+        self.assertTrue(start == """INSERT FOO (a, b, c) VALUES """)
+        self.assertTrue(values == "(%s, %s, %s)")
+        self.assertTrue(end == "")
         
     def test_ping(self):
         self.connection.ping()
 
     def test_literal_int(self):
-        self.failUnless("2" == self.connection.literal(2))
+        self.assertEqual("2", self.connection.literal(2))
     
     def test_literal_float(self):
-        self.failUnless("3.1415" == self.connection.literal(3.1415))
+        self.assertEqual("3.1415", self.connection.literal(3.1415))
         
     def test_literal_string(self):
-        self.failUnless("'foo'" == self.connection.literal("foo"))
+        self.assertEqual("'foo'", self.connection.literal("foo"))
         
     
 if __name__ == '__main__':
@@ -112,4 +112,4 @@
         gc.enable()
         gc.set_debug(gc.DEBUG_LEAK)
     unittest.main()
-    print '''"Huh-huh, he said 'unit'." -- Butthead'''
+    print('''"Huh-huh, he said 'unit'." -- Butthead''')
Index: MySQLdb/setup_posix.py
===================================================================
--- MySQLdb/setup_posix.py	(revision 633)
+++ MySQLdb/setup_posix.py	(working copy)
@@ -88,4 +88,4 @@
     return metadata, ext_options
 
 if __name__ == "__main__":
-    print """You shouldn't be running this directly; it is used by setup.py."""
+    print("""You shouldn't be running this directly; it is used by setup.py.""")
Index: MySQLdb/MySQLdb/converters.py
===================================================================
--- MySQLdb/MySQLdb/converters.py	(revision 633)
+++ MySQLdb/MySQLdb/converters.py	(working copy)
@@ -39,7 +39,6 @@
 from MySQLdb.times import datetime_to_sql, timedelta_to_sql, \
      timedelta_or_None, datetime_or_None, date_or_None, \
      mysql_timestamp_converter
-from types import InstanceType
 import array
 import datetime
 from decimal import Decimal
@@ -90,10 +89,10 @@
     """
     if obj.__class__ in conv:
         return conv[obj.__class__](obj, conv)
-    classes = [ key for key in conv.keys()
+    classes = [ key for key in list(conv.keys())
                 if isinstance(obj, key) ]
     if not classes:
-        return conv[types.StringType](obj, conv)
+        return conv[bytes](obj, conv)
     conv[obj.__class__] = conv[classes[0]]
     return conv[classes[0]](obj, conv)
 
@@ -102,16 +101,15 @@
 
 simple_type_encoders = {
     int: object_to_sql,
-    long: object_to_sql,
     float: float_to_sql,
     type(None): None_to_sql,
-    unicode: unicode_to_sql,
+    str: unicode_to_sql,
     object: instance_to_sql,
     bool: bool_to_sql,
     datetime.datetime: datetime_to_sql,
     datetime.timedelta: timedelta_to_sql,
     set: Set_to_sql,
-    str: object_to_quoted_sql, # default
+    bytes: object_to_quoted_sql, # default
     }
 
 # This is for MySQL column types that can be converted directly
@@ -145,7 +143,7 @@
 # on the stack will be checked.
 
 def default_decoder(field):
-    return str
+    return bytes
 
 def default_encoder(value):
     return object_to_quoted_sql
@@ -167,13 +165,13 @@
     if field.type not in character_types:
         return None
     if field.charsetnr == 63: # BINARY
-        return str
+        return bytes
     
     charset = field.result.connection.character_set_name()
-    def char_to_unicode(s):
+    def bytes_to_unicode(s):
         return s.decode(charset)
     
-    return char_to_unicode
+    return bytes_to_unicode
 
 default_decoders = [
     character_decoder,
Index: MySQLdb/MySQLdb/cursors.py
===================================================================
--- MySQLdb/MySQLdb/cursors.py	(revision 633)
+++ MySQLdb/MySQLdb/cursors.py	(working copy)
@@ -159,13 +159,13 @@
         del self.messages[:]
         db = self._get_db()
         charset = db.character_set_name()
-        if isinstance(query, unicode):
-            query = query.encode(charset)
         try:
             if args is not None:
                 query = query % tuple(map(self.connection.literal, args))
+            if isinstance(query, str):
+                query = query.encode(charset)
             result = self._query(query)
-        except TypeError, msg:
+        except TypeError as msg:
             if msg.args[0] in ("not enough arguments for format string",
                                "not all arguments converted"):
                 self.messages.append((self.ProgrammingError, msg.args[0]))
@@ -208,8 +208,8 @@
         if not args:
             return
         charset = self.connection.character_set_name()
-        if isinstance(query, unicode):
-            query = query.encode(charset)
+        #if isinstance(query, str):
+        #query = query.encode(charset)
         matched = INSERT_VALUES.match(query)
         if not matched:
             self.rowcount = sum([ self.execute(query, arg) for arg in args ])
@@ -225,7 +225,7 @@
             self._executed = multirow_query
             self.rowcount = int(self._query(multirow_query))
 
-        except TypeError, msg:
+        except TypeError as msg:
             if msg.args[0] in ("not enough arguments for format string",
                                "not all arguments converted"):
                 self.messages.append((self.ProgrammingError, msg.args[0]))
@@ -278,7 +278,7 @@
         for index, arg in enumerate(args):
             query = "SET @_%s_%d=%s" % (procname, index,
                                         self.connection.literal(arg))
-            if isinstance(query, unicode):
+            if isinstance(query, str):
                 query = query.encode(charset)
             self._query(query)
             self.nextset()
@@ -286,7 +286,7 @@
         query = "CALL %s(%s)" % (procname,
                                  ','.join(['@_%s_%d' % (procname, i)
                                            for i in range(len(args))]))
-        if isinstance(query, unicode):
+        if isinstance(query, str):
             query = query.encode(charset)
         self._query(query)
         self._executed = query
@@ -369,7 +369,7 @@
             row = value
         else:
             self.errorhandler(self, self.ProgrammingError,
-                              "unknown scroll mode %s" % `mode`)
+                              "unknown scroll mode %s" % repr(mode))
         if row < 0 or row >= len(self._rows):
             self.errorhandler(self, IndexError, "out of range")
         self.rownumber = row
Index: MySQLdb/MySQLdb/exceptions.py
===================================================================
--- MySQLdb/MySQLdb/exceptions.py	(revision 633)
+++ MySQLdb/MySQLdb/exceptions.py	(working copy)
@@ -12,16 +12,13 @@
 # It would make things much cleaner for Python-2.5, but breaks older.
 
 try:
-    from exceptions import Exception, StandardError, Warning
+    from exceptions import Exception, Exception, Warning
 except ImportError:
-    import sys
-    e = sys.modules['exceptions']
-    StandardError = e.StandardError
-    Warning = e.Warning
+    pass
     
 from MySQLdb.constants import ER
 
-class MySQLError(StandardError):
+class MySQLError(Exception):
     
     """Exception related to operation with MySQL."""
 
@@ -114,4 +111,4 @@
 _map_error(NotSupportedError, ER.WARNING_NOT_COMPLETE_ROLLBACK,
            ER.NOT_SUPPORTED_YET, ER.FEATURE_DISABLED, ER.UNKNOWN_STORAGE_ENGINE)
 
-del StandardError, _map_error, ER
+del _map_error, ER
Index: MySQLdb/MySQLdb/times.py
===================================================================
--- MySQLdb/MySQLdb/times.py	(revision 633)
+++ MySQLdb/MySQLdb/times.py	(working copy)
@@ -191,7 +191,7 @@
     
     """
     try:
-        return date(*map(int, obj.split('-', 2)))
+        return date(*list(map(int, obj.split(b'-', 2))))
     except ValueError:
         return None
 
@@ -238,4 +238,4 @@
 if __name__ == "__main__":
     import doctest
     doctest.testmod()
-    
\ No newline at end of file
+    
Index: MySQLdb/MySQLdb/connections.py
===================================================================
--- MySQLdb/MySQLdb/connections.py	(revision 633)
+++ MySQLdb/MySQLdb/connections.py	(working copy)
@@ -29,7 +29,7 @@
         connection.messages.append(error)
     del cursor
     del connection
-    raise errorclass, errorvalue
+    raise errorclass(errorvalue)
 
 
 class Connection(object):
