def __init__(self, decoders, readers, default): if not readers: readers = frozendict() self._readers = readers self._decoders = decoders.with_pair( 'TaggedValue', self._handle_tagged_value) if not default: default = TaggedValue self._default = default
def loads(string, readers=frozendict(), default=None): """Interpret an edn string. See https://github.com/edn-format/edn. :param string: A UTF-8 encoded string containing edn data. :param readers: A map from tag symbols to callables. For '#foo bar' whatever callable the Symbol('foo') key is mapped to will be called with 'bar'. There are default readers for #inst and #uuid, which can be overridden here. :param default: Called whenever we come across a tagged value that is not mentioned in `readers'. It gets the symbol and the interpreted value, and whatever it returns is how that value will be interpreted. :return: A Python object representing the edn element. """ return from_terms(parse(string), readers, default)
def from_terms(term, readers=frozendict(), default=None): """Take a parsed edn term and return a useful Python object. :param term: A parsed edn term, probably got from `edn.parse`. :param readers: A map from tag symbols to callables. For '#foo bar' whatever callable the Symbol('foo') key is mapped to will be called with 'bar'. There are default readers for #inst and #uuid, which can be overridden here. :param default: callable taking a symbol & value that's called when there's no tag symbol in the reader. It gets the symbol and the interpreted value, and can return whatever Python object it pleases. :return: Whatever term gets decoded to. """ builder = _Decoder(_DECODERS, DEFAULT_READERS.merge(readers), default) build = getattr(term, 'build', None) if build: return build(builder) return builder.leafData(term)(term)
def load(stream, readers=frozendict(), default=None): """Interpret an edn stream. Load an edn stream lazily as a Python iterator over the elements defined in stream. We can do this because edn does not mandate a top-level enclosing element. See https://github.com/edn-format/edn. :param stream: A file-like object of UTF-8 encoded text containing edn data. :param readers: A map from tag symbols to callables. For '#foo bar' whatever callable the Symbol('foo') key is mapped to will be called with 'bar'. There are default readers for #inst and #uuid, which can be overridden here. :param default: Called whenever we come across a tagged value that is not mentioned in `readers'. It gets the symbol and the interpreted value, and whatever it returns is how that value will be interpreted. :return: An iterator of Python objects representing the edn elements in the stream. """ for term in parse_stream(stream): yield from_terms(term, readers, default=default)
def test_readers(self): ast = TaggedValue(Symbol('foo'), String('bar')) result = from_terms( ast, frozendict({Symbol('foo'): lambda x: list(reversed(x))})) self.assertEqual([u'r', u'a', u'b'], result)
def test_map(self): self.assertEqual( frozendict({1: 2, 3: 4}), from_terms(Map(((1, 2), (3, 4)))))
def test_map(self): self.assertEqual(Map(((1, 2), (3, 4))), to_terms({1: 2, 3: 4})) self.assertEqual( Map(((1, 2), (3, 4))), to_terms(frozendict({1: 2, 3: 4})))
def test_structure(self): self.assertEqual(set([1, 2, 3]), loads('#{1 2 3}')) self.assertEqual(frozendict({1: 2, 3: 4}), loads('{1 2, 3 4}')) self.assertEqual( frozendict({Keyword(Symbol('foo')): Symbol('bar')}), loads('{:foo bar}'))
def identity(x): return x def constantly(x): return lambda *a, **kw: x _DECODERS = frozendict({ '.tuple.': lambda *a: a, 'Character': unicode, 'ExactFloat': Decimal, 'String': unicode, 'Vector': tuple, 'List': tuple, 'Map': frozendict, 'Nil': constantly(None), 'Set': frozenset, 'Symbol': Symbol, 'Keyword': Keyword, }) INST = Symbol('inst') UUID = Symbol('uuid') DEFAULT_READERS = frozendict({ INST: iso8601.parse_date, UUID: uuid.UUID,