From 08c38fd58b3858acc5da50eb6dbe0a0bb18fc09c Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Wed, 10 Jul 2013 01:32:57 -0700 Subject: [PATCH] Remove explicit dependencies. --- .travis.yml | 2 +- setup.py | 6 ++-- sqlalchemy_utils/types/color.py | 16 ++++++++++- sqlalchemy_utils/types/phone_number.py | 23 +++++++++++++-- tests/__init__.py | 6 +--- tests/test_coercion_listener.py | 39 -------------------------- tests/test_color.py | 17 +++++++++-- tests/test_number_range.py | 13 ++++++++- tests/test_phonenumber_type.py | 23 ++++++++++++++- tests/test_utility_functions.py | 2 +- 10 files changed, 91 insertions(+), 56 deletions(-) delete mode 100644 tests/test_coercion_listener.py diff --git a/.travis.yml b/.travis.yml index a3033fb..c5a04a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,6 @@ python: - 2.7 - 3.3 install: - - pip install -q -e ".[test]" --use-mirrors + - pip install -q -e ".[test,color,phone]" --use-mirrors script: - python setup.py test diff --git a/setup.py b/setup.py index 316bf8a..c61996c 100644 --- a/setup.py +++ b/setup.py @@ -45,8 +45,6 @@ setup( install_requires=[ 'six', 'SQLAlchemy>=0.8.0', - 'phonenumbers3k==5.6b1', - 'colour>=0.0.3' ], extras_require={ 'test': [ @@ -55,7 +53,9 @@ setup( 'Jinja2>=2.3', 'docutils>=0.10', 'flexmock>=0.9.7', - ] + ], + 'phone': ['phonenumbers3k==5.6b1'], + 'color': ['colour>=0.0.3'] }, cmdclass={'test': PyTest}, classifiers=[ diff --git a/sqlalchemy_utils/types/color.py b/sqlalchemy_utils/types/color.py index 5e539ea..6f77816 100644 --- a/sqlalchemy_utils/types/color.py +++ b/sqlalchemy_utils/types/color.py @@ -1,6 +1,15 @@ import six -from colour import Color from sqlalchemy import types +from sqlalchemy_utils import ImproperlyConfigured + + +try: + import colour + from colour import Color + +except ImportError: + colour = None + Color = None class ColorType(types.TypeDecorator): @@ -12,6 +21,11 @@ class ColorType(types.TypeDecorator): impl = types.Unicode(20) def __init__(self, max_length=20, *args, **kwargs): + # Bail if colour is not found. + if colour is None: + raise ImproperlyConfigured( + "'colour' is required to use 'ColorType'") + super(ColorType, self).__init__(*args, **kwargs) self.impl = types.Unicode(max_length) diff --git a/sqlalchemy_utils/types/phone_number.py b/sqlalchemy_utils/types/phone_number.py index 425d058..01ce9af 100644 --- a/sqlalchemy_utils/types/phone_number.py +++ b/sqlalchemy_utils/types/phone_number.py @@ -1,9 +1,18 @@ import six -import phonenumbers from sqlalchemy import types +from sqlalchemy_utils import ImproperlyConfigured -class PhoneNumber(phonenumbers.phonenumber.PhoneNumber): +try: + import phonenumbers + from phonenumbers.phonenumber import PhoneNumber as BasePhoneNumber + +except ImportError: + phonenumbers = None + BasePhoneNumber = object + + +class PhoneNumber(BasePhoneNumber): ''' Extends a PhoneNumber class from `Python phonenumbers library`_. Adds different phone number formats to attributes, so they can be easily used @@ -21,6 +30,11 @@ class PhoneNumber(phonenumbers.phonenumber.PhoneNumber): Country code of the phone number. ''' def __init__(self, raw_number, country_code=None): + # Bail if phonenumbers is not found. + if phonenumbers is None: + raise ImproperlyConfigured( + "'phonenumbers' is required to use 'PhoneNumber'") + self._phone_number = phonenumbers.parse(raw_number, country_code) super(PhoneNumber, self).__init__( country_code=self._phone_number.country_code, @@ -66,6 +80,11 @@ class PhoneNumberType(types.TypeDecorator): impl = types.Unicode(20) def __init__(self, country_code='US', max_length=20, *args, **kwargs): + # Bail if phonenumbers is not found. + if phonenumbers is None: + raise ImproperlyConfigured( + "'phonenumbers' is required to use 'PhoneNumberType'") + super(PhoneNumberType, self).__init__(*args, **kwargs) self.country_code = country_code self.impl = types.Unicode(max_length) diff --git a/tests/__init__.py b/tests/__init__.py index 3a117ec..12c3d2c 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,10 +5,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy_utils import ( - InstrumentedList, - PhoneNumberType, -) +from sqlalchemy_utils import InstrumentedList @sa.event.listens_for(sa.engine.Engine, 'before_cursor_execute') @@ -47,7 +44,6 @@ class TestCase(object): __tablename__ = 'user' id = sa.Column(sa.Integer, autoincrement=True, primary_key=True) name = sa.Column(sa.Unicode(255)) - phone_number = sa.Column(PhoneNumberType()) class Category(self.Base): __tablename__ = 'category' diff --git a/tests/test_coercion_listener.py b/tests/test_coercion_listener.py deleted file mode 100644 index 4f978f0..0000000 --- a/tests/test_coercion_listener.py +++ /dev/null @@ -1,39 +0,0 @@ -from colour import Color -import sqlalchemy as sa -from sqlalchemy_utils import ( - ColorType, - NumberRangeType, - NumberRange, - PhoneNumberType, - PhoneNumber, - coercion_listener -) -from tests import TestCase - - -class TestCoercionListener(TestCase): - def create_models(self): - class User(self.Base): - __tablename__ = 'user' - id = sa.Column(sa.Integer, primary_key=True) - fav_color = sa.Column(ColorType) - phone_number = sa.Column(PhoneNumberType(country_code='FI')) - number_of_friends = sa.Column(NumberRangeType) - - def __repr__(self): - return 'User(%r)' % self.id - - self.User = User - sa.event.listen( - sa.orm.mapper, 'mapper_configured', coercion_listener - ) - - def test_scalar_attributes_get_coerced_to_objects(self): - user = self.User( - fav_color='white', - phone_number='050111222', - number_of_friends='[12, 18]' - ) - assert isinstance(user.fav_color, Color) - assert isinstance(user.phone_number, PhoneNumber) - assert isinstance(user.number_of_friends, NumberRange) diff --git a/tests/test_color.py b/tests/test_color.py index 46d8aeb..de82cb2 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -1,9 +1,11 @@ -from colour import Color +from pytest import mark import sqlalchemy as sa -from sqlalchemy_utils import ColorType +from sqlalchemy_utils import ColorType, coercion_listener +from sqlalchemy_utils.types import color from tests import TestCase +@mark.xfail('color.colour is None') class TestColorType(TestCase): def create_models(self): class Document(self.Base): @@ -15,8 +17,12 @@ class TestColorType(TestCase): return 'Document(%r)' % self.id self.Document = Document + sa.event.listen(sa.orm.mapper, 'mapper_configured', coercion_listener) + def test_color_parameter_processing(self): + from colour import Color + document = self.Document( bg_color=Color(u'white') ) @@ -26,3 +32,10 @@ class TestColorType(TestCase): document = self.session.query(self.Document).first() assert document.bg_color.hex == Color(u'white').hex + + def test_scalar_attributes_get_coerced_to_objects(self): + from colour import Color + + document = self.Document(bg_color='white') + + assert isinstance(document.bg_color, Color) diff --git a/tests/test_number_range.py b/tests/test_number_range.py index d4d63b2..3e00d86 100644 --- a/tests/test_number_range.py +++ b/tests/test_number_range.py @@ -1,7 +1,12 @@ import sqlalchemy as sa from pytest import raises -from sqlalchemy_utils import NumberRangeType, NumberRange, NumberRangeException from tests import TestCase +from sqlalchemy_utils import ( + NumberRangeType, + NumberRange, + NumberRangeException, + coercion_listener +) class TestNumberRangeType(TestCase): @@ -15,6 +20,7 @@ class TestNumberRangeType(TestCase): return 'Building(%r)' % self.id self.Building = Building + sa.event.listen(sa.orm.mapper, 'mapper_configured', coercion_listener) def test_save_number_range(self): building = self.Building( @@ -42,6 +48,11 @@ class TestNumberRangeType(TestCase): building = self.session.query(self.Building).first() assert building.persons_at_night is None + def test_scalar_attributes_get_coerced_to_objects(self): + building = self.Building(persons_at_night='[12, 18]') + + assert isinstance(building.persons_at_night, NumberRange) + class TestNumberRange(object): def test_equality_operator(self): diff --git a/tests/test_phonenumber_type.py b/tests/test_phonenumber_type.py index 41bbfcd..661182a 100644 --- a/tests/test_phonenumber_type.py +++ b/tests/test_phonenumber_type.py @@ -1,7 +1,11 @@ +from pytest import mark from tests import TestCase -from sqlalchemy_utils import PhoneNumber +import sqlalchemy as sa +from sqlalchemy_utils import PhoneNumberType, PhoneNumber, coercion_listener +from sqlalchemy_utils.types import phone_number +@mark.xfail('phone_number.phonenumbers is None') class TestPhoneNumber(object): def setup_method(self, method): self.valid_phone_numbers = [ @@ -45,7 +49,19 @@ class TestPhoneNumber(object): assert phone_number.__str__() == phone_number.national.encode('utf-8') +@mark.xfail('phone_number.phonenumbers is None') class TestPhoneNumberType(TestCase): + + def create_models(self): + class User(self.Base): + __tablename__ = 'user' + id = sa.Column(sa.Integer, autoincrement=True, primary_key=True) + name = sa.Column(sa.Unicode(255)) + phone_number = sa.Column(PhoneNumberType()) + + self.User = User + sa.event.listen(sa.orm.mapper, 'mapper_configured', coercion_listener) + def setup_method(self, method): super(TestPhoneNumberType, self).setup_method(method) self.phone_number = PhoneNumber( @@ -83,3 +99,8 @@ class TestPhoneNumberType(TestCase): {'param': user.id} ) assert result.first()[0] is None + + def test_scalar_attributes_get_coerced_to_objects(self): + user = self.User(phone_number='050111222') + + assert isinstance(user.phone_number, PhoneNumber) diff --git a/tests/test_utility_functions.py b/tests/test_utility_functions.py index 17fb538..30f307f 100644 --- a/tests/test_utility_functions.py +++ b/tests/test_utility_functions.py @@ -17,7 +17,7 @@ class TestDeferExcept(TestCase): class TestFindNonIndexedForeignKeys(TestCase): - dns = 'postgres://postgres@localhost/sqlalchemy_utils_test' + # dns = 'postgres://postgres@localhost/sqlalchemy_utils_test' def create_models(self): class User(self.Base):