Created
July 3, 2014 15:20
-
-
Save anfedorov/f91bd2118d8a381ea7ea to your computer and use it in GitHub Desktop.
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
import re | |
from collections import namedtuple | |
from decimal import Decimal | |
from dateutil import parser | |
from functools import partial | |
token_parsers = { | |
'decimal': Decimal, | |
'payee': str, | |
'str': str, | |
'float': float, | |
'int': int, | |
'isodate': lambda x: parser.parse(x).date(), | |
'any': lambda x: x, | |
} | |
def parse(schema, x=None): | |
if x is None: | |
return partial(parse, schema) | |
schema = schema.strip() | |
if schema == '': | |
return None | |
if schema in token_parsers: | |
return token_parsers[schema](x) | |
if schema.startswith('{') and schema.endswith('}'): # is dict | |
m = re.match(r'^{\s*(.+)\s*:\s*(.+)\s*,\s*\.\.\.\s*}$', schema) | |
if m: # is repeating | |
assert isinstance(x, dict), 'not dict: %s' % x | |
return {parse(m.group(1), k): parse(m.group(2), v) for k, v in x.items()} | |
else: # is specific | |
return parse_object(schema[1:-1], x) | |
if schema.startswith('[') and schema.endswith(']'): # is list | |
m = re.match(r'^\[(.*),\s*\.\.\.\s*\]$', schema) | |
if m: # is repeating | |
assert isinstance(x, list), 'not list: %s' % x | |
return [parse(m.group(1), v) for v in x] | |
else: # is optional | |
assert False, 'list schema must end in ", ..."' | |
if schema.startswith('(') and schema.endswith(')'): # is tuple | |
schema_parts = schema[1:-1].split(',') | |
assert len(schema_parts) == len(x), 'bad number of parts: %s' % x | |
return tuple(parse(s, v) for s, v in zip(schema_parts, x)) | |
m = re.match(r'^(\w+)\((.*)\)$', schema) # is namedtuple | |
if m: | |
param = namedtuple('param', ['name', 'type']) | |
params = [param(*p.split(':')) for p in m.group(2).split(',')] | |
assert len(params) == len(x), 'bad len: %s' % x | |
nt = namedtuple(m.group(1), [p.name.strip() for p in params]) | |
return nt(*[parse(p.type.strip(), v) for p, v in zip(params, x)]) | |
assert False, 'what is this i dont even: "%s"' % schema | |
def find_closing(string, open_index): | |
open_char = string[open_index] | |
assert open_char in '{([' | |
close_char = '}' if open_char == '{' else \ | |
')' if open_char == '(' else \ | |
']' | |
depth = 1 | |
index = open_index + 1 | |
while depth > 0: | |
if string[index] == open_char: | |
depth += 1 | |
elif string[index] == close_char: | |
depth -= 1 | |
index += 1 | |
return index | |
def find_comma(string, index): | |
while string[index] != ',': | |
if string[index] in '{([': | |
index = find_closing(string, index) | |
else: | |
index += 1 | |
return index | |
def parse_object(schema, o): | |
schema = schema.strip() | |
if not schema: | |
return {} | |
retval = {} | |
m = re.match(r'^(\'([^\']+)\':\s*)(.*?),', schema, flags=re.S) or re.match(r'^"([^"]+)":\s*(.)', schema) | |
assert m, 'bad literal object: %s' % schema | |
key = m.group(2) | |
assert key in o, 'key %s not found in %s' % (key, o) | |
value_start = len(m.group(1)) | |
if schema[value_start] in '([{': | |
value_end = find_closing(schema, value_start) | |
retval[key] = parse(schema[value_start:value_end], o[key]) | |
else: | |
value_end = find_comma(schema, value_start) | |
retval[key] = parse(schema[value_start:value_end], o[key]) | |
retval.update(parse_object(schema[value_end+1:], o)) | |
return retval | |
print parse('isodate', '2014-01-01') | |
print parse('[isodate, ...]', ['2014-01-01', '2014-02-01']) | |
print parse('{payee: decimal, ...}', {'one': 1, 'two': 2}) | |
print parse('(str, str)', ('foo', 'bar')) | |
print parse('Point(x:float, y:float)', (1, 2)) | |
schema = """ | |
{ | |
'foo': [isodate, ...], | |
'bar': { | |
payee: decimal, | |
... | |
}, | |
'baz': Player(name:str, score:int), | |
} | |
""" | |
obj = { | |
'foo': ['2014-01-01', '2014-02-01'], | |
'bar': { | |
'p1': 3, | |
'p2': 2, | |
'p3': 1, | |
}, | |
'baz': ('bob', 10), | |
} | |
print parse(schema, obj) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment