Merge branch 'JackWink-master'

This commit is contained in:
Konsta Vesterinen
2016-05-20 09:58:31 +03:00
5 changed files with 65 additions and 7 deletions

View File

@@ -82,6 +82,7 @@ from .types import ( # noqa
Password,
PasswordType,
PhoneNumber,
PhoneNumberParseException,
PhoneNumberType,
register_composites,
remove_composite_listeners,

View File

@@ -20,7 +20,11 @@ from .pg_composite import ( # noqa
register_composites,
remove_composite_listeners
)
from .phone_number import PhoneNumber, PhoneNumberType # noqa
from .phone_number import ( # noqa
PhoneNumber,
PhoneNumberParseException,
PhoneNumberType
)
from .range import ( # noqa
DateRangeType,
DateTimeRangeType,

View File

@@ -1,4 +1,5 @@
from sqlalchemy import types
import six
from sqlalchemy import exc, types
from ..exceptions import ImproperlyConfigured
from ..utils import str_coercible
@@ -7,9 +8,23 @@ from .scalar_coercible import ScalarCoercible
try:
import phonenumbers
from phonenumbers.phonenumber import PhoneNumber as BasePhoneNumber
from phonenumbers.phonenumberutil import NumberParseException
except ImportError:
phonenumbers = None
BasePhoneNumber = object
NumberParseException = Exception
class PhoneNumberParseException(NumberParseException, exc.DontWrapMixin):
'''
Wraps exceptions from phonenumbers with SQLAlchemy's DontWrapMixin
so we get more meaningful exceptions on validation failure instead of the
StatementException
Clients can catch this as either a PhoneNumberParseException or
NumberParseException from the phonenumbers library.
'''
pass
@str_coercible
@@ -62,9 +77,23 @@ class PhoneNumber(BasePhoneNumber):
# Bail if phonenumbers is not found.
if phonenumbers is None:
raise ImproperlyConfigured(
"'phonenumbers' is required to use 'PhoneNumber'")
"'phonenumbers' is required to use 'PhoneNumber'"
)
try:
self._phone_number = phonenumbers.parse(raw_number, region)
except NumberParseException as e:
# Wrap exception so SQLAlchemy doesn't swallow it as a
# StatementError
#
# Worth noting that if -1 shows up as the error_type
# it's likely because the API has changed upstream and these
# bindings need to be updated.
raise PhoneNumberParseException(
getattr(e, 'error_type', -1),
six.text_type(e)
)
self._phone_number = phonenumbers.parse(raw_number, region)
super(PhoneNumber, self).__init__(
country_code=self._phone_number.country_code,
national_number=self._phone_number.national_number,
@@ -132,7 +161,8 @@ class PhoneNumberType(types.TypeDecorator, ScalarCoercible):
# Bail if phonenumbers is not found.
if phonenumbers is None:
raise ImproperlyConfigured(
"'phonenumbers' is required to use 'PhoneNumberType'")
"'phonenumbers' is required to use 'PhoneNumberType'"
)
super(PhoneNumberType, self).__init__(*args, **kwargs)
self.region = region

View File

@@ -46,7 +46,8 @@ class TestMakeOrderByDeterministic(object):
def test_column_property(self, session, User):
query = session.query(User).order_by(User.email_lower)
query = make_order_by_deterministic(query)
assert_contains('lower("user".name), "user".id ASC', query)
assert_contains('lower("user".name) AS lower_1', query)
assert_contains('lower_1, "user".id ASC', query)
def test_unique_column(self, session, User):
query = session.query(User).order_by(User.email)

View File

@@ -2,7 +2,12 @@ import pytest
import six
import sqlalchemy as sa
from sqlalchemy_utils import PhoneNumber, PhoneNumberType, types # noqa
from sqlalchemy_utils import ( # noqa
PhoneNumber,
PhoneNumberParseException,
PhoneNumberType,
types
)
@pytest.fixture
@@ -77,6 +82,23 @@ class TestPhoneNumber(object):
except:
pass
def test_invalid_phone_numbers_throw_dont_wrap_exception(
self,
session,
User
):
try:
session.execute(
User.__table__.insert().values(
name='Someone',
phone_number='abc'
)
)
except PhoneNumberParseException:
pass
except:
assert False
def test_phone_number_attributes(self):
number = PhoneNumber('+358401234567')
assert number.e164 == u'+358401234567'