Skip to content
15 changes: 15 additions & 0 deletions docs/samples/adv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

from glom import glom


class Seq(object):
"Applies a list of specs to a single target, returning a list of results"
def __init__(self, *subspecs):
self.subspecs = subspecs

def glomit(self, target, scope):
return [scope[glom](target, spec, scope) for spec in self.subspecs]

output = glom('1', Seq(float, int))

assert output == [1.0, 1]
96 changes: 96 additions & 0 deletions docs/samples/intro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
data = \
{
"status": "ok",
"version": "v1",
"sched_date": "2016-05-12",
"table_name": "Outer table",
"data": [
{
"data": [
{
"year": "2009",
"values": [
{
"version": "v1",
"data": [
{
"data": [
{
"year": "2009",
"values": [42],
}
],
"sched_date": "2014-03-18",
"table_name": "Inner Table",
"unit": "Percent"
}
]
}
],
}
],
}
]
}

print(data)

data.get('data', [{}])[0]

res = data.get('data', [{}])[0].get('data')[0].get('values', [{}])[0].get('data')[0].get('sched_date')
print(res)


## The transformation

from glom import glom

target = {'ID': 2, 'data': {'isoDate': '1999-01-01'}}

response = {'id': glom(target, 'ID'),
'date': glom(target, 'data.isoDate')}

response = glom(target, {'id': 'ID',
'date': 'data.isoDate'})
print(response)


def get_response(in_data):
ret = {}

try:
ret['id'] = in_data['ID']
except (KeyError, TypeError):
pass # TODO

try:
data = in_data['data']
except KeyError:
pass # TODO

try:
ret['date'] = data['isoDate']
except (KeyError, TypeError):
pass # TODO

return ret

assert get_response(target) == response


EMAIL_SPEC = {'id': 'email_id',
'email': 'email_addr',
'type': 'email_type'}


def get_email(email_id):
email = Email.objects.get(email_id=email_id)
return glom(email, EMAIL_SPEC)


def get_all_emails(**filters):
queryset = Email.objects.filter(**filters)

all_emails_spec = {'results': [EMAIL_SPEC]}

return glom(queryset, all_emails_spec)
58 changes: 58 additions & 0 deletions docs/samples/mid_specs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# assignment

from glom import glom, Assign

target = {'a': [{'b': 'c'}, {'d': None}]}

# let's set the 'd' key to 'e'
glom(target, Assign('a.1.d', 'e'))

assert target['a'][1]['d'] == 'e'


from glom import Spec

# let's assign one target value to another target value
glom(target, Assign('a.1.d', Spec('a.0.b')))

assert target['a'] == [{'b': 'c'}, {'d': 'c'}]


# Iter

from glom import glom, Iter

target = ['1', '2', '1', '3']

spec = Iter().map(int).unique().all()

output = glom(target, spec)
assert output == [1, 2, 3]

# T

from glom import glom, T

target = {'a': {'b': {'c': 'd'}}}

spec = T['a']['b']['c']

output = glom(target, spec)
assert output == 'd'

output = glom(target, T['a']['b']['c'].upper())
assert output == 'D'

output = glom(target, T['z'].upper(), default=None)
assert output is None


class Contact(object):
def __init__(self, name, details_dict):
self.name = name
self.details = details_dict

frank = Contact('Frank', {'emails': ['FRANK@NothingButHotdogs.BIZ']})

output = glom(frank, T.details['emails'][0].lower())
assert output == 'frank@nothingbuthotdogs.biz'
81 changes: 81 additions & 0 deletions glom/test/demo_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
structured demo code as tests to keep us honest in ohnly showing working code
"""
import json

import pytest

from glom import Ref, Match, Switch, Auto, T, glom, Or, GlomError, M, And



def test_json_check():
# we can define a self-recursive spec that
# checks a data structure for JSON compatibility
json_spec = Match(Ref(
'json', Switch({
dict: {str: Ref('json')},
list: [Ref('json')],
Or(int, float, str): T,
})
))
glom({'customers': ['alice', 'bob']}, json_spec)
# why might we want to do this?
# first, we get a nice error tree
# we also have very fine grained control over behaviors
# maybe we want to be more strict than the standard library
json.dumps({1: 1}) # {"1": 1}
with pytest.raises(GlomError):
glom({1: 1}, json_spec)
# maybe we want to customize a little bit
# for example, lets say we want to ensure that
# integers are 'float-safe' (can be losslessly
# converted to an IEEE 754 double precision float)
json_spec = Match(Ref(
'json', Switch({
dict: {str: Ref('json')},
list: [Ref('json')],
Or(float, str): T,
And(int, M > - 2 ** 52, M < 2 ** 52): T,
})
))
glom(2**51, json_spec)
with pytest.raises(GlomError):
glom(2**52, json_spec)
# maybe we want to dump an object but still
# check that its output is valid
class Message(object):
def __init__(self, value):
self.value = value

def as_dict(self):
return {'type': 'message', 'value': self.value}

json_spec = Match(Ref(
'json', Switch({
dict: {str: Ref('json')},
list: [Ref('json')],
Or(int, float, str): T,
T.as_dict: Auto((T.as_dict(), Match(Ref('json'))))
})
))

# what would the alternative, self-recursive version look like?
def json_spec_r(val):
if type(val) is dict:
for key in val:
assert isinstance(key, str)
return {
key: json_spec_r(sub_val) for key, sub_val in val.items()}
if type(val) is list:
return [json_spec_r(sub_val) for sub_val in val]
if type(val) in (int, float, str):
return val
if hasattr(val, "as_dict"):
return json_spec_r(val.as_dict())
raise TypeError('no match')

glom(Message(1), json_spec)
glom(Message(Message(1)), json_spec)
with pytest.raises(GlomError):
glom(Message(object), json_spec)