forked from eduardostalinho/jsonbender
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/control-flow' into develop
- Loading branch information
Showing
3 changed files
with
192 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
from jsonbender.core import Bender | ||
from jsonbender.selectors import K | ||
|
||
|
||
class If(Bender): | ||
""" | ||
Takes a condition bender, and two benders (both default to K(None)). | ||
If the condition bender evaluates to true, return the value of the first | ||
bender. If it evaluates to false, return the value of the second bender. | ||
Example: | ||
``` | ||
if_ = If(S('country') == K('China'), S('first_name'), S('last_name')) | ||
if_({'country': 'China', | ||
'first_name': 'Li', | ||
'last_name': 'Na'}) # -> 'Li' | ||
if_({'country': 'Brazil', | ||
'first_name': 'Gustavo', | ||
'last_name': 'Kuerten'}) # -> 'Kuerten' | ||
``` | ||
""" | ||
|
||
def __init__(self, condition, when_true=K(None), when_false=K(None)): | ||
self.condition = condition | ||
self.when_true = when_true | ||
self.when_false = when_false | ||
|
||
def execute(self, val): | ||
return (self.when_true(val) | ||
if self.condition(val) | ||
else self.when_false(val)) | ||
|
||
|
||
class Alternation(Bender): | ||
""" | ||
Take any number of benders, and return the value of the first one that | ||
doesn't raise a LookupError (KeyError, IndexError etc.). | ||
If all benders raise LookupError, re-raise the last raised exception. | ||
Example: | ||
``` | ||
b = Alternation(S(1), S(0), S('key1')) | ||
b(['a', 'b']) # -> 'b' | ||
b(['a']) # -> 'a' | ||
b([]) # -> KeyError | ||
b({}) # -> KeyError | ||
b({'key1': 23}) # -> 23 | ||
``` | ||
""" | ||
|
||
def __init__(self, *benders): | ||
self.benders = benders | ||
|
||
def execute(self, source): | ||
exc = ValueError() | ||
for bender in self.benders: | ||
try: | ||
result = bender(source) | ||
except LookupError as e: | ||
exc = e | ||
else: | ||
return result | ||
else: | ||
raise exc | ||
|
||
|
||
class Switch(Bender): | ||
""" | ||
Take a key bender, a 'case' container of benders and a default bender | ||
(optional). | ||
The value returned by the key bender is used to get a bender from the | ||
case container, which then returns the result. | ||
If the key is not in the case container, the default is used. | ||
If it's unavailable, raise the original LookupError. | ||
Example: | ||
``` | ||
b = Switch(S('service'), | ||
{'twitter': S('handle'), | ||
'mastodon': S('handle') + K('@') + S('server')}, | ||
default=S('email')) | ||
b({'service': 'twitter', 'handle': 'etandel'}) # -> 'etandel' | ||
b({'service': 'mastodon', 'handle': 'etandel', | ||
'server': 'mastodon.social'}) # -> '[email protected]' | ||
b({'service': 'facebook', | ||
'email': '[email protected]'}) # -> '[email protected]' | ||
``` | ||
""" | ||
|
||
def __init__(self, key_bender, cases, default=None): | ||
self.key_bender = key_bender | ||
self.cases = cases | ||
self.default = default | ||
|
||
def execute(self, source): | ||
key = self.key_bender(source) | ||
try: | ||
bender = self.cases[key] | ||
except LookupError: | ||
if self.default: | ||
bender = self.default | ||
else: | ||
raise | ||
|
||
return bender(source) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
from operator import add | ||
import unittest | ||
|
||
from jsonbender import Context, K, S, bend | ||
from jsonbender.control_flow import If, Alternation, Switch | ||
from jsonbender.test import BenderTestMixin | ||
|
||
|
||
class TestIf(BenderTestMixin, unittest.TestCase): | ||
def setUp(self): | ||
self.na_li = {'country': 'China', | ||
'first_name': 'Li', | ||
'last_name': 'Na'} | ||
self.guga = {'country': 'Brazil', | ||
'first_name': 'Gustavo', | ||
'last_name': 'Kuerten'} | ||
|
||
def test_if_true(self): | ||
if_ = If(S('country') == K('China'), S('first_name'), S('last_name')) | ||
self.assert_bender(if_, self.na_li, 'Li') | ||
|
||
def test_if_false(self): | ||
if_ = If(S('country') == K('China'), S('first_name'), S('last_name')) | ||
self.assert_bender(if_, self.guga, 'Kuerten') | ||
|
||
def test_if_true_default(self): | ||
if_ = If(S('country') == K('China'), when_false=S('last_name')) | ||
self.assert_bender(if_, self.na_li, None) | ||
|
||
def test_if_false_default(self): | ||
if_ = If(S('country') == K('China'), S('first_name')) | ||
self.assert_bender(if_, self.guga, None) | ||
|
||
|
||
class TestAlternation(BenderTestMixin, unittest.TestCase): | ||
def test_empty_benders(self): | ||
self.assertRaises(ValueError, Alternation(), {}) | ||
|
||
def test_matches(self): | ||
bender = Alternation(S(1), S(0), S('key1')) | ||
self.assert_bender(bender, ['a', 'b'], 'b') | ||
self.assert_bender(bender, ['a'], 'a') | ||
self.assert_bender(bender, {'key1': 23}, 23) | ||
|
||
def test_no_match(self): | ||
self.assertRaises(IndexError, Alternation(S(1)), []) | ||
self.assertRaises(KeyError, Alternation(S(1)), {}) | ||
|
||
|
||
class TestSwitch(BenderTestMixin, unittest.TestCase): | ||
def test_match(self): | ||
bender = Switch(S('service'), | ||
{'twitter': S('handle'), | ||
'mastodon': S('handle') + K('@') + S('server')}, | ||
default=S('email')) | ||
|
||
self.assert_bender(bender, | ||
{'service': 'twitter', 'handle': 'etandel'}, | ||
'etandel') | ||
self.assert_bender(bender, | ||
{'service': 'mastodon', | ||
'handle': 'etandel', | ||
'server': 'mastodon.social'}, | ||
'[email protected]') | ||
|
||
def test__no_match_with_default(self): | ||
bender = Switch(S('service'), | ||
{'twitter': S('handle'), | ||
'mastodon': S('handle') + K('@') + S('server')}, | ||
default=S('email')) | ||
self.assert_bender(bender, | ||
{'service': 'facebook', | ||
'email': '[email protected]'}, | ||
'[email protected]') | ||
|
||
def test__no_match_without_default(self): | ||
self.assertRaises(KeyError, Switch(S('key'), {}), {'key': None}) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() | ||
|