diff --git a/.travis.yml b/.travis.yml index 7685239..a3033fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: python python: - - 2.5 - 2.6 - 2.7 + - 3.3 install: - - pip install -q -e . --use-mirrors + - pip install -q -e ".[test]" --use-mirrors script: - python setup.py test diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index bfade57..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,6 +0,0 @@ --r requirements.txt -pytest==2.2.3 -Pygments==1.2 -Jinja2==2.3 -docutils>=0.10 -flexmock>=0.9.7 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 6565908..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -SQLAlchemy>=0.8.0 -phonenumbers>=5.4b1 -colour==0.0.2 diff --git a/setup.py b/setup.py index 41294ca..4fb9576 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,6 @@ SQLAlchemy-Utils Various utility functions and custom data types for SQLAlchemy. """ - from setuptools import setup, Command import subprocess @@ -22,6 +21,7 @@ class PyTest(Command): errno = subprocess.call(['py.test']) raise SystemExit(errno) + setup( name='SQLAlchemy-Utils', version='0.13.3', @@ -37,11 +37,26 @@ setup( zip_safe=False, include_package_data=True, platforms='any', - install_requires=[ - 'SQLAlchemy>=0.8.0', - 'phonenumbers>=5.4b1', - 'colour==0.0.2' + dependency_links=[ + # 5.6 supports python 3.x / pending release + 'git+git://github.com/daviddrysdale/python-phonenumbers.git@python3' + '#egg=phonenumbers3k-5.6b1', ], + install_requires=[ + 'six', + 'SQLAlchemy>=0.8.0', + 'phonenumbers3k==5.6b1', + 'colour>=0.0.3' + ], + extras_require={ + 'test': [ + 'pytest==2.2.3', + 'Pygments>=1.2', + 'Jinja2>=2.3', + 'docutils>=0.10', + 'flexmock>=0.9.7', + ] + }, cmdclass={'test': PyTest}, classifiers=[ 'Environment :: Web Environment', diff --git a/sqlalchemy_utils/merge.py b/sqlalchemy_utils/merge.py index eaf2854..5e7f2c8 100644 --- a/sqlalchemy_utils/merge.py +++ b/sqlalchemy_utils/merge.py @@ -1,3 +1,4 @@ +import six import sqlalchemy as sa from sqlalchemy.engine import reflection from sqlalchemy.orm import object_session, mapperlib @@ -51,7 +52,7 @@ class Merger(object): def raw_merge(self, session, table, old_values, new_values): conditions = [] - for key, value in old_values.items(): + for key, value in six.iteritems(old_values): conditions.append(getattr(table.c, key) == value) sql = ( table diff --git a/sqlalchemy_utils/operators.py b/sqlalchemy_utils/operators.py index 4fc801a..6d457a1 100644 --- a/sqlalchemy_utils/operators.py +++ b/sqlalchemy_utils/operators.py @@ -1,3 +1,4 @@ +import six import sqlalchemy as sa diff --git a/sqlalchemy_utils/types.py b/sqlalchemy_utils/types.py index fed2530..3ab9524 100644 --- a/sqlalchemy_utils/types.py +++ b/sqlalchemy_utils/types.py @@ -1,3 +1,4 @@ +import six import phonenumbers from colour import Color from functools import wraps @@ -56,7 +57,7 @@ class PhoneNumber(phonenumbers.phonenumber.PhoneNumber): return self.national def __str__(self): - return unicode(self).encode('utf-8') + return six.text_type(self.national).encode('utf-8') class PhoneNumberType(types.TypeDecorator): @@ -95,7 +96,7 @@ class ColorType(types.TypeDecorator): Changes Color objects to a string representation on the way in and changes them back to Color objects on the way out. """ - STORE_FORMAT = 'hex' + STORE_FORMAT = u'hex' impl = types.Unicode(20) def __init__(self, max_length=20, *args, **kwargs): @@ -104,7 +105,7 @@ class ColorType(types.TypeDecorator): def process_bind_param(self, value, dialect): if value: - return getattr(value, self.STORE_FORMAT) + return six.text_type(getattr(value, self.STORE_FORMAT)) return value def process_result_value(self, value, dialect): @@ -125,22 +126,22 @@ class ScalarListException(Exception): class ScalarListType(types.TypeDecorator): impl = sa.UnicodeText() - def __init__(self, coerce_func=unicode, separator=u','): - self.separator = unicode(separator) + def __init__(self, coerce_func=six.text_type, separator=u','): + self.separator = six.text_type(separator) self.coerce_func = coerce_func def process_bind_param(self, value, dialect): # Convert list of values to unicode separator-separated list # Example: [1, 2, 3, 4] -> u'1, 2, 3, 4' if value is not None: - if any(self.separator in unicode(item) for item in value): + if any(self.separator in six.text_type(item) for item in value): raise ScalarListException( "List values can't contain string '%s' (its being used as " "separator. If you wish for scalar list values to contain " "these strings, use a different separator string." ) return self.separator.join( - map(unicode, value) + map(six.text_type, value) ) def process_result_value(self, value, dialect): @@ -148,9 +149,9 @@ class ScalarListType(types.TypeDecorator): if value == u'': return [] # coerce each value - return map( + return list(map( self.coerce_func, value.split(self.separator) - ) + )) class EmailType(sa.types.TypeDecorator): @@ -181,7 +182,7 @@ class NumberRangeType(types.TypeDecorator): def process_result_value(self, value, dialect): if value: - if not isinstance(value, basestring): + if not isinstance(value, six.string_types): value = NumberRange.from_range_object(value) else: return NumberRange.from_normalized_str(value) @@ -189,7 +190,7 @@ class NumberRangeType(types.TypeDecorator): def coercion_listener(self, target, value, oldvalue, initiator): if value is not None and not isinstance(value, NumberRange): - if isinstance(value, basestring): + if isinstance(value, six.string_types): value = NumberRange.from_normalized_str(value) else: raise TypeError @@ -252,7 +253,7 @@ class NumberRange(object): min_value, max_value = map( lambda a: int(a.strip()), values ) - except ValueError, e: + except ValueError as e: raise NumberRangeException(e.message) if value[0] == '(': @@ -274,8 +275,8 @@ class NumberRange(object): min_value, max_value = map( lambda a: int(a.strip()), values ) - except ValueError, e: - raise NumberRangeException(e.message) + except ValueError as e: + raise NumberRangeException(str(e)) return cls(min_value, max_value) @property diff --git a/tests/__init__.py b/tests/__init__.py index 6155c6a..2f65a82 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -20,9 +20,7 @@ def count_sql_calls(conn, cursor, statement, parameters, context, executemany): class TestCase(object): def setup_method(self, method): - self.engine = create_engine( - 'postgres://postgres@localhost/sqlalchemy_utils_test' - ) + self.engine = create_engine('sqlite:///:memory:') self.connection = self.engine.connect() self.Base = declarative_base() diff --git a/tests/test_scalar_list.py b/tests/test_scalar_list.py index 0d1ea14..d928c7f 100644 --- a/tests/test_scalar_list.py +++ b/tests/test_scalar_list.py @@ -1,3 +1,4 @@ +import six import sqlalchemy as sa from sqlalchemy_utils import ScalarListType from pytest import raises @@ -33,7 +34,7 @@ class TestScalarUnicodeList(TestCase): class User(self.Base): __tablename__ = 'user' id = sa.Column(sa.Integer, primary_key=True) - some_list = sa.Column(ScalarListType(unicode)) + some_list = sa.Column(ScalarListType(six.text_type)) def __repr__(self): return 'User(%r)' % self.id