Skip to content

Commit

Permalink
Add Python 3 compatibility.
Browse files Browse the repository at this point in the history
  • Loading branch information
baztian committed Apr 26, 2015
1 parent affe3ae commit ccf890c
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 31 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ deploy:
python:
- '2.6'
- '2.7'
- '3.4'

env:
matrix:
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
.. image:: https://img.shields.io/coveralls/baztian/jaydebeapi/master.svg
:target: https://coveralls.io/r/baztian/jaydebeapi

.. image:: https://img.shields.io/badge/python-2.6,_2.7-blue.svg
.. image:: https://img.shields.io/badge/python-2.6,_2.7,_3.4-blue.svg
:target: https://pypi.python.org/pypi/JayDeBeApi

.. image:: https://img.shields.io/badge/jython-2.5.3,_2.7--rc1-blue.svg
Expand Down
67 changes: 51 additions & 16 deletions jaydebeapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,47 @@
__version__ = ".".join(str(i) for i in __version_info__)

import datetime
import exceptions
import glob
import os
import time
import re
import sys
import warnings

PY2 = sys.version_info[0] == 2

if PY2:
# Ideas stolen from the six python 2 and 3 compatibility layer
def exec_(_code_, _globs_=None, _locs_=None):
"""Execute code in a namespace."""
if _globs_ is None:
frame = sys._getframe(1)
_globs_ = frame.f_globals
if _locs_ is None:
_locs_ = frame.f_locals
del frame
elif _locs_ is None:
_locs_ = _globs_
exec("""exec _code_ in _globs_, _locs_""")

exec_("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")
else:
def reraise(tp, value, tb=None):
if value is None:
value = tp()
else:
value = tp(value)
if tb:
raise value.with_traceback(tb)
raise value

if PY2:
string_type = basestring
else:
string_type = str

# Mapping from java.sql.Types attribute name to attribute value
_jdbc_name_to_const = None

Expand All @@ -48,7 +81,7 @@ def _handle_sql_exception_jython():
exc_type = DatabaseError
else:
exc_type = InterfaceError
raise exc_type, exc_info[1], exc_info[2]
reraise(exc_type, exc_info[1], exc_info[2])

def _jdbc_connect_jython(jclassname, jars, libs, *args):
if _jdbc_name_to_const is None:
Expand Down Expand Up @@ -112,7 +145,7 @@ def _handle_sql_exception_jpype():
exc_type = DatabaseError
else:
exc_type = InterfaceError
raise exc_type, exc_info[1], exc_info[2]
reraise(exc_type, exc_info[1], exc_info[2])

def _jdbc_connect_jpype(jclassname, jars, libs, *driver_args):
import jpype
Expand Down Expand Up @@ -191,7 +224,7 @@ def __init__(self, *values):
self.values = values
for type_name in values:
if type_name in DBAPITypeObject._mappings:
raise ValueError, "Non unique mapping for type '%s'" % type_name
raise ValueError("Non unique mapping for type '%s'" % type_name)
DBAPITypeObject._mappings[type_name] = self
def __cmp__(self, other):
if other in self.values:
Expand Down Expand Up @@ -239,10 +272,10 @@ def _map_jdbc_type_to_dbapi(cls, jdbc_type_const):
ROWID = DBAPITypeObject('ROWID')

# DB-API 2.0 Module Interface Exceptions
class Error(exceptions.StandardError):
class Error(Exception):
pass

class Warning(exceptions.StandardError):
class Warning(Exception):
pass

class InterfaceError(Error):
Expand Down Expand Up @@ -311,15 +344,15 @@ def connect(jclassname, driver_args, jars=None, libs=None):
libs: Dll/so filenames or sequence of dlls/sos used as shared
library by the JDBC driver
"""
if isinstance(driver_args, basestring):
if isinstance(driver_args, string_type):
driver_args = [ driver_args ]
if jars:
if isinstance(jars, basestring):
if isinstance(jars, string_type):
jars = [ jars ]
else:
jars = []
if libs:
if isinstance(libs, basestring):
if isinstance(libs, string_type):
libs = [ libs ]
else:
libs = []
Expand Down Expand Up @@ -347,7 +380,7 @@ def __init__(self, jconn, converters):

def close(self):
if self._closed:
raise Error
raise Error()
self.jconn.close()
self._closed = True

Expand Down Expand Up @@ -436,7 +469,7 @@ def _set_stmt_parms(self, prep_stmt, parameters):

def execute(self, operation, parameters=None):
if self._connection._closed:
raise Error
raise Error()
if not parameters:
parameters = ()
self._close_last()
Expand Down Expand Up @@ -467,7 +500,7 @@ def executemany(self, operation, seq_of_parameters):

def fetchone(self):
if not self._rs:
raise Error
raise Error()
if not self._rs.next():
return None
row = []
Expand All @@ -480,14 +513,14 @@ def fetchone(self):

def fetchmany(self, size=None):
if not self._rs:
raise Error
raise Error()
if size is None:
size = self.arraysize
# TODO: handle SQLException if not supported by db
self._rs.setFetchSize(size)
rows = []
row = None
for i in xrange(size):
for i in range(size):
row = self.fetchone()
if row is None:
break
Expand Down Expand Up @@ -554,7 +587,9 @@ def to_py(rs, col):
java_val = rs.getObject(col)
if java_val is None:
return
if isinstance(java_val, (basestring, int, long, float, bool)):
if PY2 and isinstance(java_val, (string_type, int, long, float, bool)):
return java_val
elif isinstance(java_val, (string_type, int, float, bool)):
return java_val
return getattr(java_val, java_method)()
return to_py
Expand All @@ -567,7 +602,7 @@ def _init_types(types_map):
global _jdbc_name_to_const
_jdbc_name_to_const = types_map
global _jdbc_const_to_name
_jdbc_const_to_name = dict((y,x) for x,y in types_map.iteritems())
_jdbc_const_to_name = dict((y,x) for x,y in types_map.items())
_init_converters(types_map)

def _init_converters(types_map):
Expand Down
7 changes: 4 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@
author_email = 'bastian.dev@gmail.com',
license = 'GNU LGPL',
url='https://github.com/baztian/jaydebeapi',
description=('A bridge from JDBC database drivers to Python DB-API.'),
long_description=file('README.rst').read(),
description=('Use JDBC database drivers from Python 2/3 or Jython with a DB-API.'),
long_description=open('README.rst').read(),
keywords = ('db api java jdbc bridge connect sql jpype jython'),
classifiers = [
'Development Status :: 3 - Alpha',
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'Programming Language :: Java',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
'Topic :: Database',
'Topic :: Software Development :: Libraries :: Java Libraries',
'Topic :: Software Development :: Libraries :: Python Modules',
Expand Down
16 changes: 11 additions & 5 deletions test/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@

_THIS_DIR = os.path.dirname(os.path.abspath(__file__))

PY26 = not sys.version_info >= (2, 7)

if PY26:
memoryview = buffer

def is_jython():
return sys.platform.lower().startswith('java')

Expand Down Expand Up @@ -140,12 +145,12 @@ def test_execute_types(self):
"BLOCKING, DBL_COL, OPENED_AT, VALID, PRODUCT_NAME) " \
"values (?, ?, ?, ?, ?, ?, ?, ?)"
d = self.dbapi
account_id = d.Timestamp(2010, 01, 26, 14, 31, 59)
account_id = d.Timestamp(2010, 1, 26, 14, 31, 59)
account_no = 20
balance = 1.2
blocking = 10.0
dbl_col = 3.5
opened_at = d.Date(2008, 02, 27)
opened_at = d.Date(2008, 2, 27)
valid = 1
product_name = u'Savings account'
parms = (account_id, account_no, balance, blocking, dbl_col,
Expand All @@ -168,7 +173,7 @@ def test_execute_type_time(self):
"OPENED_AT_TIME) " \
"values (?, ?, ?, ?)"
d = self.dbapi
account_id = d.Timestamp(2010, 01, 26, 14, 31, 59)
account_id = d.Timestamp(2010, 1, 26, 14, 31, 59)
account_no = 20
balance = 1.2
opened_at_time = d.Time(13, 59, 59)
Expand Down Expand Up @@ -209,7 +214,8 @@ def test_execute_type_blob(self):
cursor = self.conn.cursor()
stmt = "insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE, " \
"STUFF) values (?, ?, ?, ?)"
stuff = self.dbapi.Binary('abcdef')
binary_stuff = 'abcdef'.encode('UTF-8')
stuff = self.dbapi.Binary(binary_stuff)
parms = ('2009-09-11 14:15:22.123450', 20, 13.1, stuff)
cursor.execute(stmt, parms)
stmt = "select STUFF from ACCOUNT where ACCOUNT_NO = ?"
Expand All @@ -218,7 +224,7 @@ def test_execute_type_blob(self):
result = cursor.fetchone()
cursor.close()
value = result[0]
self.assertEqual(value, buffer('abcdef'))
self.assertEqual(value, memoryview(binary_stuff))

@unittest.skipIf(is_jython(), "requires python")
class SqlitePyTest(SqliteTestBase, unittest.TestCase):
Expand Down
12 changes: 6 additions & 6 deletions test/test_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_sql_exception_on_execute(self):
try:
cursor.execute("dummy stmt")
fail("expected exception")
except jaydebeapi.DatabaseError, e:
except jaydebeapi.DatabaseError as e:
self.assertEquals(str(e), "java.sql.SQLException: expected")

def test_runtime_exception_on_execute(self):
Expand All @@ -62,37 +62,37 @@ def test_runtime_exception_on_execute(self):
try:
cursor.execute("dummy stmt")
fail("expected exception")
except jaydebeapi.InterfaceError, e:
except jaydebeapi.InterfaceError as e:
self.assertEquals(str(e), "java.lang.RuntimeException: expected")

def test_sql_exception_on_commit(self):
self.conn.jconn.mockExceptionOnCommit("java.sql.SQLException", "expected")
try:
self.conn.commit()
fail("expected exception")
except jaydebeapi.DatabaseError, e:
except jaydebeapi.DatabaseError as e:
self.assertEquals(str(e), "java.sql.SQLException: expected")

def test_runtime_exception_on_commit(self):
self.conn.jconn.mockExceptionOnCommit("java.lang.RuntimeException", "expected")
try:
self.conn.commit()
fail("expected exception")
except jaydebeapi.InterfaceError, e:
except jaydebeapi.InterfaceError as e:
self.assertEquals(str(e), "java.lang.RuntimeException: expected")

def test_sql_exception_on_rollback(self):
self.conn.jconn.mockExceptionOnRollback("java.sql.SQLException", "expected")
try:
self.conn.rollback()
fail("expected exception")
except jaydebeapi.DatabaseError, e:
except jaydebeapi.DatabaseError as e:
self.assertEquals(str(e), "java.sql.SQLException: expected")

def test_runtime_exception_on_rollback(self):
self.conn.jconn.mockExceptionOnRollback("java.lang.RuntimeException", "expected")
try:
self.conn.rollback()
fail("expected exception")
except jaydebeapi.InterfaceError, e:
except jaydebeapi.InterfaceError as e:
self.assertEquals(str(e), "java.lang.RuntimeException: expected")

0 comments on commit ccf890c

Please sign in to comment.