Add Python 3 compatibility.
parent
affe3ae688
commit
453eb61598
|
|
@ -16,6 +16,7 @@ deploy:
|
||||||
python:
|
python:
|
||||||
- '2.6'
|
- '2.6'
|
||||||
- '2.7'
|
- '2.7'
|
||||||
|
- '3.4'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
matrix:
|
matrix:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
.. image:: https://img.shields.io/coveralls/baztian/jaydebeapi/master.svg
|
.. image:: https://img.shields.io/coveralls/baztian/jaydebeapi/master.svg
|
||||||
:target: https://coveralls.io/r/baztian/jaydebeapi
|
: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
|
:target: https://pypi.python.org/pypi/JayDeBeApi
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/jython-2.5.3,_2.7--rc1-blue.svg
|
.. image:: https://img.shields.io/badge/jython-2.5.3,_2.7--rc1-blue.svg
|
||||||
|
|
@ -168,6 +168,9 @@ Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
- Next version - unreleased
|
- Next version - unreleased
|
||||||
|
|
||||||
|
- Python 3 support (requires JPype1 >= 0.6.0).
|
||||||
|
|
||||||
- 0.1.6 - 2015-04-10
|
- 0.1.6 - 2015-04-10
|
||||||
|
|
||||||
- Fix Jython handling of Java exceptions that don't subclass python Exception
|
- Fix Jython handling of Java exceptions that don't subclass python Exception
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
cat requirements-python.txt >> requirements.txt
|
if [ -f requirements-python-${TRAVIS_PYTHON_VERSION}.txt ]
|
||||||
|
then
|
||||||
|
cat requirements-python-${TRAVIS_PYTHON_VERSION}.txt >> requirements.txt
|
||||||
|
fi
|
||||||
ln -s $VIRTUAL_ENV $HOME/myvirtualenv
|
ln -s $VIRTUAL_ENV $HOME/myvirtualenv
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ __version_info__ = (0, 1, 6)
|
||||||
__version__ = ".".join(str(i) for i in __version_info__)
|
__version__ = ".".join(str(i) for i in __version_info__)
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import exceptions
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
@ -29,6 +28,40 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
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
|
# Mapping from java.sql.Types attribute name to attribute value
|
||||||
_jdbc_name_to_const = None
|
_jdbc_name_to_const = None
|
||||||
|
|
||||||
|
|
@ -48,7 +81,7 @@ def _handle_sql_exception_jython():
|
||||||
exc_type = DatabaseError
|
exc_type = DatabaseError
|
||||||
else:
|
else:
|
||||||
exc_type = InterfaceError
|
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):
|
def _jdbc_connect_jython(jclassname, jars, libs, *args):
|
||||||
if _jdbc_name_to_const is None:
|
if _jdbc_name_to_const is None:
|
||||||
|
|
@ -112,7 +145,7 @@ def _handle_sql_exception_jpype():
|
||||||
exc_type = DatabaseError
|
exc_type = DatabaseError
|
||||||
else:
|
else:
|
||||||
exc_type = InterfaceError
|
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):
|
def _jdbc_connect_jpype(jclassname, jars, libs, *driver_args):
|
||||||
import jpype
|
import jpype
|
||||||
|
|
@ -191,7 +224,7 @@ class DBAPITypeObject(object):
|
||||||
self.values = values
|
self.values = values
|
||||||
for type_name in values:
|
for type_name in values:
|
||||||
if type_name in DBAPITypeObject._mappings:
|
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
|
DBAPITypeObject._mappings[type_name] = self
|
||||||
def __cmp__(self, other):
|
def __cmp__(self, other):
|
||||||
if other in self.values:
|
if other in self.values:
|
||||||
|
|
@ -239,10 +272,10 @@ DATETIME = DBAPITypeObject('TIMESTAMP')
|
||||||
ROWID = DBAPITypeObject('ROWID')
|
ROWID = DBAPITypeObject('ROWID')
|
||||||
|
|
||||||
# DB-API 2.0 Module Interface Exceptions
|
# DB-API 2.0 Module Interface Exceptions
|
||||||
class Error(exceptions.StandardError):
|
class Error(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Warning(exceptions.StandardError):
|
class Warning(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class InterfaceError(Error):
|
class InterfaceError(Error):
|
||||||
|
|
@ -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
|
libs: Dll/so filenames or sequence of dlls/sos used as shared
|
||||||
library by the JDBC driver
|
library by the JDBC driver
|
||||||
"""
|
"""
|
||||||
if isinstance(driver_args, basestring):
|
if isinstance(driver_args, string_type):
|
||||||
driver_args = [ driver_args ]
|
driver_args = [ driver_args ]
|
||||||
if jars:
|
if jars:
|
||||||
if isinstance(jars, basestring):
|
if isinstance(jars, string_type):
|
||||||
jars = [ jars ]
|
jars = [ jars ]
|
||||||
else:
|
else:
|
||||||
jars = []
|
jars = []
|
||||||
if libs:
|
if libs:
|
||||||
if isinstance(libs, basestring):
|
if isinstance(libs, string_type):
|
||||||
libs = [ libs ]
|
libs = [ libs ]
|
||||||
else:
|
else:
|
||||||
libs = []
|
libs = []
|
||||||
|
|
@ -347,7 +380,7 @@ class Connection(object):
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self._closed:
|
if self._closed:
|
||||||
raise Error
|
raise Error()
|
||||||
self.jconn.close()
|
self.jconn.close()
|
||||||
self._closed = True
|
self._closed = True
|
||||||
|
|
||||||
|
|
@ -436,7 +469,7 @@ class Cursor(object):
|
||||||
|
|
||||||
def execute(self, operation, parameters=None):
|
def execute(self, operation, parameters=None):
|
||||||
if self._connection._closed:
|
if self._connection._closed:
|
||||||
raise Error
|
raise Error()
|
||||||
if not parameters:
|
if not parameters:
|
||||||
parameters = ()
|
parameters = ()
|
||||||
self._close_last()
|
self._close_last()
|
||||||
|
|
@ -467,7 +500,7 @@ class Cursor(object):
|
||||||
|
|
||||||
def fetchone(self):
|
def fetchone(self):
|
||||||
if not self._rs:
|
if not self._rs:
|
||||||
raise Error
|
raise Error()
|
||||||
if not self._rs.next():
|
if not self._rs.next():
|
||||||
return None
|
return None
|
||||||
row = []
|
row = []
|
||||||
|
|
@ -480,14 +513,14 @@ class Cursor(object):
|
||||||
|
|
||||||
def fetchmany(self, size=None):
|
def fetchmany(self, size=None):
|
||||||
if not self._rs:
|
if not self._rs:
|
||||||
raise Error
|
raise Error()
|
||||||
if size is None:
|
if size is None:
|
||||||
size = self.arraysize
|
size = self.arraysize
|
||||||
# TODO: handle SQLException if not supported by db
|
# TODO: handle SQLException if not supported by db
|
||||||
self._rs.setFetchSize(size)
|
self._rs.setFetchSize(size)
|
||||||
rows = []
|
rows = []
|
||||||
row = None
|
row = None
|
||||||
for i in xrange(size):
|
for i in range(size):
|
||||||
row = self.fetchone()
|
row = self.fetchone()
|
||||||
if row is None:
|
if row is None:
|
||||||
break
|
break
|
||||||
|
|
@ -554,7 +587,9 @@ def _java_to_py(java_method):
|
||||||
java_val = rs.getObject(col)
|
java_val = rs.getObject(col)
|
||||||
if java_val is None:
|
if java_val is None:
|
||||||
return
|
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 java_val
|
||||||
return getattr(java_val, java_method)()
|
return getattr(java_val, java_method)()
|
||||||
return to_py
|
return to_py
|
||||||
|
|
@ -567,7 +602,7 @@ def _init_types(types_map):
|
||||||
global _jdbc_name_to_const
|
global _jdbc_name_to_const
|
||||||
_jdbc_name_to_const = types_map
|
_jdbc_name_to_const = types_map
|
||||||
global _jdbc_const_to_name
|
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)
|
_init_converters(types_map)
|
||||||
|
|
||||||
def _init_converters(types_map):
|
def _init_converters(types_map):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
JPype1==0.6.0
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
JPype1==0.6.0
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
JPype1==0.5.7
|
|
||||||
7
setup.py
7
setup.py
|
|
@ -32,16 +32,17 @@ setup(
|
||||||
author_email = 'bastian.dev@gmail.com',
|
author_email = 'bastian.dev@gmail.com',
|
||||||
license = 'GNU LGPL',
|
license = 'GNU LGPL',
|
||||||
url='https://github.com/baztian/jaydebeapi',
|
url='https://github.com/baztian/jaydebeapi',
|
||||||
description=('A bridge from JDBC database drivers to Python DB-API.'),
|
description=('Use JDBC database drivers from Python 2/3 or Jython with a DB-API.'),
|
||||||
long_description=file('README.rst').read(),
|
long_description=open('README.rst').read(),
|
||||||
keywords = ('db api java jdbc bridge connect sql jpype jython'),
|
keywords = ('db api java jdbc bridge connect sql jpype jython'),
|
||||||
classifiers = [
|
classifiers = [
|
||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 4 - Beta',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
|
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
|
||||||
'Programming Language :: Java',
|
'Programming Language :: Java',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 2',
|
'Programming Language :: Python :: 2',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
'Topic :: Database',
|
'Topic :: Database',
|
||||||
'Topic :: Software Development :: Libraries :: Java Libraries',
|
'Topic :: Software Development :: Libraries :: Java Libraries',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,14 @@ import unittest2 as unittest
|
||||||
|
|
||||||
_THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
_THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
PY26 = not sys.version_info >= (2, 7)
|
||||||
|
|
||||||
def is_jython():
|
def is_jython():
|
||||||
return sys.platform.lower().startswith('java')
|
return sys.platform.lower().startswith('java')
|
||||||
|
|
||||||
|
if PY26 and not is_jython:
|
||||||
|
memoryview = buffer
|
||||||
|
|
||||||
class IntegrationTestBase(object):
|
class IntegrationTestBase(object):
|
||||||
|
|
||||||
def sql_file(self, filename):
|
def sql_file(self, filename):
|
||||||
|
|
@ -140,12 +145,12 @@ class IntegrationTestBase(object):
|
||||||
"BLOCKING, DBL_COL, OPENED_AT, VALID, PRODUCT_NAME) " \
|
"BLOCKING, DBL_COL, OPENED_AT, VALID, PRODUCT_NAME) " \
|
||||||
"values (?, ?, ?, ?, ?, ?, ?, ?)"
|
"values (?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
d = self.dbapi
|
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
|
account_no = 20
|
||||||
balance = 1.2
|
balance = 1.2
|
||||||
blocking = 10.0
|
blocking = 10.0
|
||||||
dbl_col = 3.5
|
dbl_col = 3.5
|
||||||
opened_at = d.Date(2008, 02, 27)
|
opened_at = d.Date(2008, 2, 27)
|
||||||
valid = 1
|
valid = 1
|
||||||
product_name = u'Savings account'
|
product_name = u'Savings account'
|
||||||
parms = (account_id, account_no, balance, blocking, dbl_col,
|
parms = (account_id, account_no, balance, blocking, dbl_col,
|
||||||
|
|
@ -168,7 +173,7 @@ class IntegrationTestBase(object):
|
||||||
"OPENED_AT_TIME) " \
|
"OPENED_AT_TIME) " \
|
||||||
"values (?, ?, ?, ?)"
|
"values (?, ?, ?, ?)"
|
||||||
d = self.dbapi
|
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
|
account_no = 20
|
||||||
balance = 1.2
|
balance = 1.2
|
||||||
opened_at_time = d.Time(13, 59, 59)
|
opened_at_time = d.Time(13, 59, 59)
|
||||||
|
|
@ -209,7 +214,8 @@ class SqliteTestBase(IntegrationTestBase):
|
||||||
cursor = self.conn.cursor()
|
cursor = self.conn.cursor()
|
||||||
stmt = "insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE, " \
|
stmt = "insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE, " \
|
||||||
"STUFF) values (?, ?, ?, ?)"
|
"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)
|
parms = ('2009-09-11 14:15:22.123450', 20, 13.1, stuff)
|
||||||
cursor.execute(stmt, parms)
|
cursor.execute(stmt, parms)
|
||||||
stmt = "select STUFF from ACCOUNT where ACCOUNT_NO = ?"
|
stmt = "select STUFF from ACCOUNT where ACCOUNT_NO = ?"
|
||||||
|
|
@ -218,7 +224,7 @@ class SqliteTestBase(IntegrationTestBase):
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
value = result[0]
|
value = result[0]
|
||||||
self.assertEqual(value, buffer('abcdef'))
|
self.assertEqual(value, memoryview(binary_stuff))
|
||||||
|
|
||||||
@unittest.skipIf(is_jython(), "requires python")
|
@unittest.skipIf(is_jython(), "requires python")
|
||||||
class SqlitePyTest(SqliteTestBase, unittest.TestCase):
|
class SqlitePyTest(SqliteTestBase, unittest.TestCase):
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class MockTest(unittest.TestCase):
|
||||||
try:
|
try:
|
||||||
cursor.execute("dummy stmt")
|
cursor.execute("dummy stmt")
|
||||||
fail("expected exception")
|
fail("expected exception")
|
||||||
except jaydebeapi.DatabaseError, e:
|
except jaydebeapi.DatabaseError as e:
|
||||||
self.assertEquals(str(e), "java.sql.SQLException: expected")
|
self.assertEquals(str(e), "java.sql.SQLException: expected")
|
||||||
|
|
||||||
def test_runtime_exception_on_execute(self):
|
def test_runtime_exception_on_execute(self):
|
||||||
|
|
@ -62,7 +62,7 @@ class MockTest(unittest.TestCase):
|
||||||
try:
|
try:
|
||||||
cursor.execute("dummy stmt")
|
cursor.execute("dummy stmt")
|
||||||
fail("expected exception")
|
fail("expected exception")
|
||||||
except jaydebeapi.InterfaceError, e:
|
except jaydebeapi.InterfaceError as e:
|
||||||
self.assertEquals(str(e), "java.lang.RuntimeException: expected")
|
self.assertEquals(str(e), "java.lang.RuntimeException: expected")
|
||||||
|
|
||||||
def test_sql_exception_on_commit(self):
|
def test_sql_exception_on_commit(self):
|
||||||
|
|
@ -70,7 +70,7 @@ class MockTest(unittest.TestCase):
|
||||||
try:
|
try:
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
fail("expected exception")
|
fail("expected exception")
|
||||||
except jaydebeapi.DatabaseError, e:
|
except jaydebeapi.DatabaseError as e:
|
||||||
self.assertEquals(str(e), "java.sql.SQLException: expected")
|
self.assertEquals(str(e), "java.sql.SQLException: expected")
|
||||||
|
|
||||||
def test_runtime_exception_on_commit(self):
|
def test_runtime_exception_on_commit(self):
|
||||||
|
|
@ -78,7 +78,7 @@ class MockTest(unittest.TestCase):
|
||||||
try:
|
try:
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
fail("expected exception")
|
fail("expected exception")
|
||||||
except jaydebeapi.InterfaceError, e:
|
except jaydebeapi.InterfaceError as e:
|
||||||
self.assertEquals(str(e), "java.lang.RuntimeException: expected")
|
self.assertEquals(str(e), "java.lang.RuntimeException: expected")
|
||||||
|
|
||||||
def test_sql_exception_on_rollback(self):
|
def test_sql_exception_on_rollback(self):
|
||||||
|
|
@ -86,7 +86,7 @@ class MockTest(unittest.TestCase):
|
||||||
try:
|
try:
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
fail("expected exception")
|
fail("expected exception")
|
||||||
except jaydebeapi.DatabaseError, e:
|
except jaydebeapi.DatabaseError as e:
|
||||||
self.assertEquals(str(e), "java.sql.SQLException: expected")
|
self.assertEquals(str(e), "java.sql.SQLException: expected")
|
||||||
|
|
||||||
def test_runtime_exception_on_rollback(self):
|
def test_runtime_exception_on_rollback(self):
|
||||||
|
|
@ -94,5 +94,5 @@ class MockTest(unittest.TestCase):
|
||||||
try:
|
try:
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
fail("expected exception")
|
fail("expected exception")
|
||||||
except jaydebeapi.InterfaceError, e:
|
except jaydebeapi.InterfaceError as e:
|
||||||
self.assertEquals(str(e), "java.lang.RuntimeException: expected")
|
self.assertEquals(str(e), "java.lang.RuntimeException: expected")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue