# Copyright (C) 2005-2012 Aaron Bentley # Chris Ball # Gianluca Montecchi # W. Trevor King # # This file is part of Bugs Everywhere. # # Bugs Everywhere is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 2 of the License, or (at your option) any # later version. # # Bugs Everywhere is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # Bugs Everywhere. If not, see . """Serializing and deserializing dictionaries of parameters. The serialized "mapfiles" should be clear, flat-text strings, and allow easy merging of independent/conflicting changes. """ import errno import json import os.path import libbe if libbe.TESTING == True: import doctest class InvalidMapfileContents (Exception): def __init__(self, contents): super(InvalidMapfileContents, self).__init__('Invalid JSON contents') self.contents = contents def generate(map, context=6): """Generate a JSON mapfile content string. Examples -------- >>> import sys >>> sys.stdout.write(generate({})) {} >>> sys.stdout.write(generate({'q':'p'})) { "q": "p" } The blank lines ensure that merging occurs independent of surrounding content. Because the mapfile format is also used by the :py:mod:`~libbe.command.serve_commands` where merging is not important, the amount of context is controllable. >>> sys.stdout.write(generate({'q':u'Fran\u00e7ais'}, context=0)) { "q": "Fran\\u00e7ais" } >>> sys.stdout.write(generate({'q':u'hello'}, context=0)) { "q": "hello" } >>> sys.stdout.write(generate( ... {'p':'really long line\\n'*10, 'q': 'the next entry'}, ... context=1)) { "p": "really long line\\nreally long line\\nreally long line\\nreally long line\\nreally long line\\nreally long line\\nreally long line\\nreally long line\\nreally long line\\nreally long line\\n", "q": "the next entry" } See Also -------- parse : inverse """ lines = json.dumps(map, sort_keys=True, indent=4).splitlines() sep = '\n' * (1 + context) return sep.join(lines) + '\n' def parse(contents): """Parse a JSON mapfile string. Examples -------- >>> parse('{"q": "p"}')['q'] u'p' >>> contents = generate({'a':'b', 'c':'d', 'e':'f'}) >>> dict = parse(contents) >>> dict['a'] u'b' >>> dict['c'] u'd' >>> dict['e'] u'f' >>> contents = generate({'q':u'Fran\u00e7ais'}) >>> dict = parse(contents) >>> dict['q'] u'Fran\\xe7ais' >>> dict = parse('a!') Traceback (most recent call last): ... InvalidMapfileContents: Invalid JSON contents See Also -------- generate : inverse """ try: return json.loads(contents) except ValueError: raise InvalidMapfileContents(contents) if libbe.TESTING == True: suite = doctest.DocTestSuite()