-
-
Save kipanshi/2024435 to your computer and use it in GitHub Desktop.
Trafaret Tutorial
This file contains hidden or 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
def trafaret_tutorial(): | |
""" | |
So, you have some structure from wild. Say this will be some JSON over API. | |
But you cant change this JSON structure. | |
""" | |
sample_data = { | |
'userNameFirst': 'Adam', | |
'userNameSecond': 'Smith', | |
'userPassword': 'supersecretpassword', | |
'userEmail': '[email protected]', | |
'userRoles': 'teacher, worker, admin', | |
} | |
""" | |
And you will not save this structure like this, actually you want something | |
like this | |
""" | |
import hashlib | |
desired_data = { | |
'name': 'Adam', | |
'second_name': 'Smith', | |
'password': hashlib.md5('supersecretpassword').hexdigest(), | |
'email': '[email protected]', | |
'roles': ['teacher', 'worker', 'admin'], | |
} | |
""" | |
Ok, so you need to convert it somehow. You will write it simple | |
""" | |
new_data = { | |
'name': sample_data['userNameFirst'], | |
'second_name': sample_data['userNameSecond'], | |
'password': hashlib.md5(sample_data['userPassword']).hexdigest(), | |
'email': sample_data['userEmail'], | |
'roles': [s.strip() for s in sample_data['userRoles'].split(',')] | |
} | |
assert new_data == desired_data, 'Uh oh' | |
""" | |
And then you will figure out that you can get much more fields | |
and decide to optimize your solution with DRY in mind | |
""" | |
FIELDS = { | |
'userNameFirst': 'name', | |
'userNameSecond': 'second_name', | |
'userEmail': 'email', | |
} | |
new_data = dict((n2, sample_data[n1]) for n1, n2 in FIELDS.items()) | |
new_data['roles'] = [s.strip() for s | |
in sample_data['userRoles'].split(',')] | |
new_data['password'] = hashlib.md5(sample_data['userPassword']).hexdigest() | |
assert new_data == desired_data, 'Uh oh' | |
""" | |
Not so bad, if you have many fields it will save your time. But now you get | |
new information - 'userEmail' is optional field. | |
And specification added field 'userTitle', | |
that must be 'bachelor' if not provided'. | |
Lets solve it! | |
""" | |
desired_data['title'] = 'Bachelor' # Update our test to new reality | |
FIELDS = { | |
'userNameFirst': 'name', | |
'userNameSecond': 'second_name', | |
'userEmail': ('email', '__optional'), | |
'userTitle': ('title', 'Bachelor'), | |
} | |
new_data = {} | |
for old, new in FIELDS.items(): | |
if isinstance(new, tuple): | |
new, default = new | |
if old not in sample_data: | |
if default == '__optional': | |
continue | |
new_data[new] = default | |
else: | |
new_data[new] = sample_data[old] | |
new_data['roles'] = [s.strip() for s | |
in sample_data['userRoles'].split(',')] | |
new_data['password'] = hashlib.md5(sample_data['userPassword']).hexdigest() | |
assert new_data == desired_data, 'Uh oh' | |
""" | |
Hm, ok, so much code, uh oh. | |
I think first variant were more straightforward. | |
""" | |
new_data = { | |
'name': sample_data['userNameFirst'], | |
'second_name': sample_data['userNameSecond'], | |
'password': hashlib.md5(sample_data['userPassword']).hexdigest(), | |
'roles': [s.strip() for s in sample_data['userRoles'].split(',')] | |
} | |
if 'userEmail' in sample_data: | |
new_data['email'] = sample_data['userEmail'] | |
new_data['title'] = sample_data.get('userTitle', 'Bachelor') | |
assert new_data == desired_data, 'Uh oh' | |
""" | |
Good old code without complicate smell, good, yeah. | |
But what if you will have more fields? | |
I mean much more, and what will you do? | |
It seems you are ready to make new cool library for this things. | |
But wait, I did! | |
""" | |
import trafaret as t | |
hash_md5 = lambda d: hashlib.md5(d).hexdigest() | |
comma_to_list = lambda d: [s.strip() for s in d.split(',')] | |
converter = t.Dict({ | |
t.Key('userNameFirst') >> 'name': t.String, | |
# Bonus: validation with regexp! | |
t.Key('userNameSecond') >> | |
'second_name': t.String(regex=r'^[a-zA-Z]+$'), | |
# You can bind as many converters as you want | |
t.Key('userPassword') >> 'password': t.String >> hash_md5, | |
# Bonus: trafaret.Email validates that string is valid email | |
t.Key('userEmail', optional=True, to_name='email'): t.Email, | |
t.Key('userTitle', default='Bachelor', to_name='title'): t.String, | |
t.Key('userRoles', to_name='roles'): t.String >> comma_to_list, | |
}) | |
assert converter.check(sample_data) == desired_data | |
sample_data['userNameSecond'] += '123' # this will raise validation error | |
try: | |
converter.check(sample_data) | |
raise Exception('Validation doesn\'t work!') | |
except t.DataError: | |
pass | |
""" | |
So go to the https://github.com/Deepwalker/trafaret and start using it! | |
""" | |
if __name__ == '__main__': | |
trafaret_tutorial() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment