Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions smartystreets_python_sdk/international_street/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from .analysis import Analysis
from .candidate import Candidate
from .client import Client
from .language_mode import LanguageMode
9 changes: 8 additions & 1 deletion smartystreets_python_sdk/international_street/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from smartystreets_python_sdk import Request
from smartystreets_python_sdk.international_street import Candidate
from smartystreets_python_sdk.international_street.language_mode import LanguageMode


class Client:
Expand Down Expand Up @@ -32,7 +33,7 @@ def build_request(self, lookup):
self.add_parameter(request, 'input_id', lookup.input_id)
self.add_parameter(request, 'country', lookup.country)
self.add_parameter(request, 'geocode', str(lookup.geocode).lower())
self.add_parameter(request, 'language', lookup.language)
self.add_parameter(request, 'language', self.normalized_language(lookup.language))
self.add_parameter(request, 'freeform', lookup.freeform)
self.add_parameter(request, 'address1', lookup.address1)
self.add_parameter(request, 'address2', lookup.address2)
Expand All @@ -57,3 +58,9 @@ def convert_candidates(raw_candidates):
def add_parameter(request, key, value):
if value and value != 'none':
request.parameters[key] = value

@staticmethod
def normalized_language(language):
if language is None:
return None
return LanguageMode.from_value(language).value
28 changes: 26 additions & 2 deletions smartystreets_python_sdk/international_street/language_mode.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
NATIVE = 'native'
from enum import Enum

LATIN = 'latin'
from smartystreets_python_sdk.exceptions import UnprocessableEntityError


class LanguageMode(Enum):
"""
A closed set of valid Language values, the closest Python equivalent to an enum-constrained field.
"""
NATIVE = 'native'
LATIN = 'latin'

@staticmethod
def from_value(value):
"""
Resolves a LanguageMode instance or a raw value (eg. from user input or config) into a LanguageMode,
matching 'native'/'latin' regardless of case.
"""
if isinstance(value, LanguageMode):
return value

for mode in LanguageMode:
if str(value).lower() == mode.value:
return mode

raise UnprocessableEntityError(
"invalid Language value; must be unset, 'native', or 'latin' (case-insensitive)")
6 changes: 5 additions & 1 deletion smartystreets_python_sdk/international_street/lookup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from smartystreets_python_sdk.exceptions import UnprocessableEntityError
from smartystreets_python_sdk.international_street.language_mode import LanguageMode


class Lookup:
Expand Down Expand Up @@ -68,6 +69,9 @@ def ensure_enough_info(self):

if self.field_is_missing(self.freeform) and self.field_is_missing(self.address1):
raise UnprocessableEntityError('Either freeform or address1 is required.')


if self.language is not None:
LanguageMode.from_value(self.language)

def add_custom_parameter(self, parameter, value):
self.custom_parameter_array[parameter] = value
36 changes: 33 additions & 3 deletions test/international_street/client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from smartystreets_python_sdk import Response
from smartystreets_python_sdk.exceptions import UnprocessableEntityError
from smartystreets_python_sdk.international_street import Lookup, Client, language_mode, Candidate
from smartystreets_python_sdk.international_street import Lookup, Client, LanguageMode, Candidate
from test.mocks import *


Expand All @@ -25,7 +25,7 @@ def test_sending_single_fully_populated_lookup(self):
lookup = Lookup()
lookup.country = '0'
lookup.geocode = True
lookup.language = language_mode.NATIVE
lookup.language = LanguageMode.NATIVE
lookup.freeform = '1'
lookup.address1 = '2'
lookup.address2 = '3'
Expand All @@ -42,7 +42,7 @@ def test_sending_single_fully_populated_lookup(self):

self.assertEqual('0', sender.request.parameters['country'])
self.assertEqual('true', sender.request.parameters['geocode'])
self.assertEqual(language_mode.NATIVE, sender.request.parameters['language'])
self.assertEqual(LanguageMode.NATIVE.value, sender.request.parameters['language'])
self.assertEqual('1', sender.request.parameters['freeform'])
self.assertEqual('2', sender.request.parameters['address1'])
self.assertEqual('3', sender.request.parameters['address2'])
Expand All @@ -55,6 +55,36 @@ def test_sending_single_fully_populated_lookup(self):
self.assertEqual('10', sender.request.parameters['features'])
self.assertEqual('11', sender.request.parameters['custom'])

def test_sending_lookup_with_mixed_case_language_value(self):
sender = RequestCapturingSender()
serializer = FakeDeserializer({})
client = Client(sender, serializer)
lookup = Lookup('1', '0')
lookup.language = 'Latin'

client.send(lookup)

self.assertEqual('latin', sender.request.parameters['language'])

def test_mixed_case_language_not_mutated(self):
sender = RequestCapturingSender()
serializer = FakeDeserializer({})
client = Client(sender, serializer)
lookup = Lookup('1', '0')
lookup.language = 'Latin'

client.send(lookup)

self.assertEqual('Latin', lookup.language)

def test_rejects_invalid_mixed_case_language_value(self):
sender = MockSender(None)
client = Client(sender, None)
lookup = Lookup('1', '0')
lookup.language = 'Klingon'

self.assertRaises(UnprocessableEntityError, client.send, lookup)

def test_empty_lookup_rejected(self):
sender = MockSender(None)
client = Client(sender, None)
Expand Down
21 changes: 21 additions & 0 deletions test/international_street/language_mode_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import unittest

from smartystreets_python_sdk.exceptions import UnprocessableEntityError
from smartystreets_python_sdk.international_street import LanguageMode


class TestLanguageMode(unittest.TestCase):
def test_from_value_resolves_mixed_case(self):
self.assertEqual(LanguageMode.LATIN, LanguageMode.from_value('Latin'))
self.assertEqual(LanguageMode.NATIVE, LanguageMode.from_value('NATIVE'))
self.assertEqual(LanguageMode.LATIN, LanguageMode.from_value('latin'))

def test_from_value_returns_language_mode_instance_unchanged(self):
self.assertEqual(LanguageMode.NATIVE, LanguageMode.from_value(LanguageMode.NATIVE))

def test_from_value_rejects_invalid_value(self):
self.assertRaises(UnprocessableEntityError, LanguageMode.from_value, 'Klingon')

def test_values_are_lowercase(self):
self.assertEqual('native', LanguageMode.NATIVE.value)
self.assertEqual('latin', LanguageMode.LATIN.value)