Skip to content

Commit

Permalink
Initial support for Python 3 with Presto.
Browse files Browse the repository at this point in the history
Closes #45

(modified by jingw)
  • Loading branch information
matthewwardrop authored and jingw committed Apr 12, 2016
1 parent f5b29d5 commit 7168c6f
Show file tree
Hide file tree
Showing 16 changed files with 77 additions and 38 deletions.
39 changes: 26 additions & 13 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
sudo: required
language: python
env:
# newest everything
- CDH=cdh5 PRESTO=0.142 SQLALCHEMY=1.0.12
# stale stuff we're still using / supporting
- CDH=cdh5 PRESTO=0.118 SQLALCHEMY=0.5.8
# every version of sqlalchemy with special code
- CDH=cdh4 PRESTO=0.118 SQLALCHEMY=0.5.8
- CDH=cdh4 PRESTO=0.118 SQLALCHEMY=0.6.9
- CDH=cdh4 PRESTO=0.118 SQLALCHEMY=0.7.10
- CDH=cdh4 PRESTO=0.118 SQLALCHEMY=0.8.7
- CDH=cdh4 PRESTO=0.118 SQLALCHEMY=1.0.8
python:
- "2.7"
matrix:
include:
# newest dependencies + a few python versions
- python: 3.5
env: CDH=cdh5 PRESTO=0.142 SQLALCHEMY=1.0.12
- python: 3.4
env: CDH=cdh5 PRESTO=0.142 SQLALCHEMY=1.0.12
- python: 3.3
env: CDH=cdh5 PRESTO=0.142 SQLALCHEMY=1.0.12
- python: 2.7
env: CDH=cdh5 PRESTO=0.142 SQLALCHEMY=1.0.12
# stale stuff we're still using / supporting
- python: 2.7
env: CDH=cdh5 PRESTO=0.118 SQLALCHEMY=0.5.8
# old python + every version of sqlalchemy with special code
- python: 2.7
env: CDH=cdh4 PRESTO=0.118 SQLALCHEMY=0.5.8
- python: 2.7
env: CDH=cdh4 PRESTO=0.118 SQLALCHEMY=0.6.9
- python: 2.7
env: CDH=cdh4 PRESTO=0.118 SQLALCHEMY=0.7.10
- python: 2.7
env: CDH=cdh4 PRESTO=0.118 SQLALCHEMY=0.8.7
- python: 2.7
env: CDH=cdh4 PRESTO=0.118 SQLALCHEMY=1.0.8
# exclude: python 3 against old libries
before_install:
- ./scripts/travis-install.sh
- pip install codecov
Expand Down
2 changes: 1 addition & 1 deletion TCLIService/TCLIService.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion TCLIService/constants.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyhive/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
__version__ = '0.1.7'
22 changes: 15 additions & 7 deletions pyhive/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@

from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import bytes
from builtins import int
from builtins import object
from builtins import range
from past.builtins import basestring
from pyhive import exc
import abc
import collections
import sys
import time
from future.utils import with_metaclass


class DBAPICursor(object):
class DBAPICursor(with_metaclass(abc.ABCMeta, object)):
"""Base class for some common DB-API logic"""
__metaclass__ = abc.ABCMeta

_STATE_NONE = 0
_STATE_RUNNING = 1
Expand Down Expand Up @@ -119,7 +125,7 @@ def fetchmany(self, size=None):
if size is None:
size = self.arraysize
result = []
for _ in xrange(size):
for _ in range(size):
one = self.fetchone()
if one is None:
break
Expand Down Expand Up @@ -176,7 +182,7 @@ def rownumber(self):
"""
return self._rownumber

def next(self):
def __next__(self):
"""Return the next row from the currently executing SQL statement using the same semantics
as :py:meth:`fetchone`. A ``StopIteration`` exception is raised when the result set is
exhausted.
Expand All @@ -187,6 +193,8 @@ def next(self):
else:
return one

next = __next__

def __iter__(self):
"""Return self to make cursors compatible to the iteration protocol."""
return self
Expand All @@ -209,7 +217,7 @@ def __cmp__(self, other):
class ParamEscaper(object):
def escape_args(self, parameters):
if isinstance(parameters, dict):
return {k: self.escape_item(v) for k, v in parameters.iteritems()}
return {k: self.escape_item(v) for k, v in parameters.items()}
elif isinstance(parameters, (list, tuple)):
return tuple(self.escape_item(x) for x in parameters)
else:
Expand All @@ -223,15 +231,15 @@ def escape_string(self, item):
# Newer SQLAlchemy checks dialect.supports_unicode_binds before encoding Unicode strings
# as byte strings. The old version always encodes Unicode as byte strings, which breaks
# string formatting here.
if isinstance(item, str):
if isinstance(item, bytes):
item = item.decode('utf-8')
# This is good enough when backslashes are literal, newlines are just followed, and the way
# to escape a single quote is to put two single quotes.
# (i.e. only special character is single quote)
return "'{}'".format(item.replace("'", "''"))

def escape_item(self, item):
if isinstance(item, (int, long, float)):
if isinstance(item, (int, float)):
return self.escape_number(item)
elif isinstance(item, basestring):
return self.escape_string(item)
Expand Down
4 changes: 2 additions & 2 deletions pyhive/exc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
]


class Error(StandardError):
class Error(Exception):
"""Exception that is the base class of all other error exceptions.
You can use this to catch all errors with one single except statement.
"""
pass


class Warning(StandardError):
class Warning(Exception):
"""Exception raised for important warnings like data truncations while inserting, etc."""
pass

Expand Down
7 changes: 6 additions & 1 deletion pyhive/presto.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import object
from pyhive import common
from pyhive.common import DBAPITypeObject
# Make all exceptions visible in this module per DB-API
Expand All @@ -15,7 +16,11 @@
import getpass
import logging
import requests
import urlparse

try: # Python 3
import urllib.parse as urlparse
except ImportError: # Python 2
import urlparse


# PEP 249 module globals
Expand Down
2 changes: 1 addition & 1 deletion pyhive/sqlalchemy_presto.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def _get_table_columns(self, connection, table_name, schema):
# call. SQLAlchemy doesn't handle this. Thus, we catch the unwrapped
# presto.DatabaseError here.
# Does the table exist?
msg = e.message.get('message') if isinstance(e.message, dict) else None
msg = e.args[0].get('message') if len(e.args) > 0 and isinstance(e.args[0], dict) else None
regex = r"Table\ \'.*{}\'\ does\ not\ exist".format(re.escape(table_name))
if msg and re.search(regex, msg):
raise exc.NoSuchTableError(table_name)
Expand Down
13 changes: 7 additions & 6 deletions pyhive/tests/dbapi_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import object
from builtins import range
from future.utils import with_metaclass
from pyhive import exc
import abc
import contextlib
Expand All @@ -22,9 +25,7 @@ def wrapped_fn(self, *args, **kwargs):
return wrapped_fn


class DBAPITestCase(object):
__metaclass__ = abc.ABCMeta

class DBAPITestCase(with_metaclass(abc.ABCMeta, object)):
@abc.abstractmethod
def connect(self):
raise NotImplementedError # pragma: no cover
Expand All @@ -42,13 +43,13 @@ def test_fetchall(self, cursor):
cursor.execute('SELECT * FROM one_row')
self.assertEqual(cursor.fetchall(), [[1]])
cursor.execute('SELECT a FROM many_rows ORDER BY a')
self.assertEqual(cursor.fetchall(), [[i] for i in xrange(10000)])
self.assertEqual(cursor.fetchall(), [[i] for i in range(10000)])

@with_cursor
def test_iterator(self, cursor):
cursor.execute('SELECT * FROM one_row')
self.assertEqual(list(cursor), [[1]])
self.assertRaises(StopIteration, cursor.next)
self.assertRaises(StopIteration, cursor.__next__)

@with_cursor
def test_description_initial(self, cursor):
Expand Down Expand Up @@ -80,7 +81,7 @@ def test_executemany(self, cursor):
for length in 1, 2:
cursor.executemany(
'SELECT %(x)d FROM one_row',
[{'x': i} for i in xrange(1, length + 1)]
[{'x': i} for i in range(1, length + 1)]
)
self.assertEqual(cursor.fetchall(), [[length]])

Expand Down
6 changes: 3 additions & 3 deletions pyhive/tests/sqlachemy_test_case.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# coding: utf-8
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import object
from distutils.version import StrictVersion
from future.utils import with_metaclass
import sqlalchemy
from sqlalchemy.exc import NoSuchTableError
from sqlalchemy.schema import Index
Expand Down Expand Up @@ -30,9 +32,7 @@ def wrapped_fn(self, *args, **kwargs):
return wrapped_fn


class SqlAlchemyTestCase(object):
__metaclass__ = abc.ABCMeta

class SqlAlchemyTestCase(with_metaclass(abc.ABCMeta, object)):
@with_engine_connection
def test_basic_query(self, engine, connection):
rows = connection.execute('SELECT * FROM one_row').fetchall()
Expand Down
2 changes: 2 additions & 0 deletions pyhive/tests/test_hive.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
import mock
import os
import unittest
import sys

_HOST = 'localhost'


@unittest.skipIf(sys.version_info.major == 3, 'Hive not yet supported on Python 3')
class TestHive(unittest.TestCase, DBAPITestCase):
__test__ = True

Expand Down
2 changes: 1 addition & 1 deletion pyhive/tests/test_presto.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_complex(self, cursor):
0.25,
'a string',
'1970-01-01 00:00:00.000',
'123',
b'123',
[1, 2],
{"1": 2, "3": 4}, # Presto converts all keys to strings so that they're valid JSON
[1, 2], # struct is returned as a list of elements
Expand Down
3 changes: 3 additions & 0 deletions pyhive/tests/test_sqlalchemy_hive.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import str
from distutils.version import StrictVersion
from pyhive.sqlalchemy_hive import HiveDate
from pyhive.sqlalchemy_hive import HiveDecimal
Expand All @@ -15,6 +16,7 @@
import decimal
import os
import sqlalchemy.types
import sys
import unittest

_ONE_ROW_COMPLEX_CONTENTS = [
Expand All @@ -36,6 +38,7 @@
]


@unittest.skipIf(sys.version_info.major == 3, 'Hive not yet supported on Python 3')
class TestSqlAlchemyHive(unittest.TestCase, SqlAlchemyTestCase):
def create_engine(self):
return create_engine('hive://localhost:10000/default')
Expand Down
3 changes: 2 additions & 1 deletion pyhive/tests/test_sqlalchemy_presto.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import str
from pyhive.tests.sqlachemy_test_case import SqlAlchemyTestCase
from pyhive.tests.sqlachemy_test_case import with_engine_connection
from sqlalchemy.engine import create_engine
Expand Down Expand Up @@ -40,7 +41,7 @@ def test_reflect_select(self, engine, connection):
0.25,
'a string',
'1970-01-01 00:00:00.000',
'123',
b'123',
[1, 2],
{"1": 2, "3": 4}, # Presto converts all keys to strings so that they're valid JSON
[1, 2], # struct is returned as a list of elements
Expand Down
3 changes: 2 additions & 1 deletion scripts/travis-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sudo -Eu hive $(dirname $0)/make_test_tables.sh
# Presto
#

sudo apt-get install -y python # Use python2 for presto server
sudo apt-get install -y oracle-java8-installer
sudo update-java-alternatives -s java-8-oracle

Expand All @@ -36,4 +37,4 @@ then
sed -i '/query.max-memory/d' presto-server-$PRESTO/etc/config.properties
fi

./presto-server-$PRESTO/bin/launcher start
/usr/bin/python2.7 presto-server-$PRESTO/bin/launcher.py start
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ def run_tests(self):
"Operating System :: OS Independent",
"Topic :: Database :: Front-Ends",
],
install_requires=[
'future',
],
extras_require={
"Presto": ['requests>=1.0.0'],
"Hive": ['sasl>=0.1.3', 'thrift>=0.8.0', 'thrift_sasl>=0.1.0'],
Expand Down

0 comments on commit 7168c6f

Please sign in to comment.