Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python 3 support #62

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b9aa053
Improved support for Python 3.
okin Jan 9, 2014
0773933
Changing the way checks for the error code are made.
okin Jan 10, 2014
4d61517
Remove MySQLdb/release.py from git
msabramo Apr 15, 2014
283299a
Support Python 2.7, 3.3, 3.4
msabramo Apr 15, 2014
7591a43
tox.ini: Remove ipdb
msabramo Apr 15, 2014
5fc7131
Some Python 3 C-API fixes.
methane Apr 16, 2014
8b8444f
Fix memory corruption on Python 3.
methane Apr 16, 2014
690e5e9
Fix getattr on result object.
methane Apr 16, 2014
80f5a4a
Fix segfault on Python 2.
methane Apr 16, 2014
2556226
Cleanup
methane Apr 16, 2014
c4d8948
Remove more macros for ancient Python.
methane Apr 16, 2014
613762c
Merge branch 'py3-capi' into py3.3support
msabramo Apr 16, 2014
a7720be
Merge branch 'support_py27_to_py34' into py3.3support
msabramo Apr 16, 2014
182011e
Merge branch 'remove_MySQLdb/release.py' into py3.3support
msabramo Apr 16, 2014
9440a0d
Handle types in a Python 3 friendly way
msabramo Apr 16, 2014
f8c046e
.travis.yml: Add py33 and py34 but allow fail
msabramo Apr 16, 2014
acc984c
Fix escaping.
methane Apr 17, 2014
9cdef71
More fix
methane Apr 17, 2014
382fb9f
Random fixes.
methane Apr 17, 2014
c66b43c
Connection.literal() always returns `str` instance.
methane Apr 17, 2014
b5780d2
self of module-level function is not NULL on Python 3.
methane Apr 17, 2014
e8c1ebb
Call decoders with unicode when field is not str and bytes.
methane Apr 17, 2014
f188e0f
Fix some tests.
methane Apr 18, 2014
5a66f79
Fix segfault on Python 2.
methane Apr 18, 2014
4a6dde8
Fix multiply by float.
methane Apr 18, 2014
67ecdc8
Fix calling Binary() with string.
methane Apr 18, 2014
67cf0a0
StandardError is removed from Python 3
methane Apr 18, 2014
312f46d
use_unicode=True by default on Python 3.
methane Apr 18, 2014
b28d58b
Don't allow test fail on Python 3.3 and 3.4
methane Apr 18, 2014
63e7052
Travis doesn't support Python 3.4 yet.
methane Apr 18, 2014
8c9f376
Use tox on Travis.
methane Apr 18, 2014
469adcf
Install python3.4-dev on travis
methane Apr 18, 2014
af18cc9
Remove pymemcompat.h
methane Apr 18, 2014
e7545c7
refactoring.
methane Apr 18, 2014
b881072
Remove unused import
methane Apr 18, 2014
56a5f1a
Add comment.
methane Apr 18, 2014
7a6b221
Python 2.6 is back.
methane Apr 20, 2014
392b548
Fix compilation error on Windows.
methane Apr 28, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 0 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion MySQLdb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
12 changes: 12 additions & 0 deletions MySQLdb/compat.py
Original file line number Diff line number Diff line change
@@ -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
37 changes: 29 additions & 8 deletions MySQLdb/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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+)")

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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():
Expand All @@ -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:
Expand Down Expand Up @@ -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.
Expand Down
78 changes: 19 additions & 59 deletions MySQLdb/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -76,8 +69,6 @@ def Unicode2Str(s, d):
is connection-dependent."""
return s.encode()

Long2Int = Thing2Str

def Float2Str(o, d):
return '%.15g' % o

Expand All @@ -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)

Expand All @@ -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,
Expand All @@ -165,25 +124,26 @@ 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
conversions[FIELD_TYPE.DECIMAL] = Decimal
conversions[FIELD_TYPE.NEWDECIMAL] = Decimal
except ImportError:
pass



Loading