diff --git a/src/hcl/api.py b/src/hcl/api.py old mode 100644 new mode 100755 index e5390f6..b0b3364 --- a/src/hcl/api.py +++ b/src/hcl/api.py @@ -42,30 +42,33 @@ def isHcl(s): raise ValueError("No HCL object could be decoded") -def load(fp): +def load(fp, object_pairs_hook=None): ''' Deserializes a file-pointer like object into a python dictionary. The contents of the file must either be JSON or HCL. :param fp: An object that has a read() function - + :param object_pairs_hook: is an optional function that behaves just like the json.load parameter does but only supports OrderedDict :returns: Dictionary ''' - return loads(fp.read()) + return loads(fp.read(), object_pairs_hook=object_pairs_hook) -def loads(s): +def loads(s, object_pairs_hook=None): ''' Deserializes a string and converts it to a dictionary. The contents of the string must either be JSON or HCL. - + + :param object_pairs_hook: is an optional function that behaves just like the json.load parameter does but only supports OrderedDict + :returns: Dictionary ''' s = u(s) if isHcl(s): - return HclParser().parse(s) + hcl_out = HclParser(object_pairs_hook=object_pairs_hook) + return hcl_out.parse(s) else: - return json.loads(s) + return json.loads(s, object_pairs_hook=object_pairs_hook) def dumps(*args, **kwargs): diff --git a/src/hcl/parser.py b/src/hcl/parser.py old mode 100644 new mode 100755 index 444497d..e23a23c --- a/src/hcl/parser.py +++ b/src/hcl/parser.py @@ -3,6 +3,7 @@ from .lexer import Lexer from ply import lex, yacc +from collections import OrderedDict import inspect @@ -74,7 +75,11 @@ def objectlist_flat(self, lt, replace): from object.go: there's a flattened list structure ''' - d = {} + + if self.object_pairs_hook == OrderedDict: + d = OrderedDict() + else: + d = {} for k, v in lt: if k in d.keys() and not replace: @@ -317,10 +322,11 @@ def p_error(self, p): raise ValueError(msg) - def __init__(self): + def __init__(self, object_pairs_hook=None): self.yacc = yacc.yacc( module=self, debug=False, optimize=1, picklefile=pickle_file ) + self.object_pairs_hook = object_pairs_hook def parse(self, s): return self.yacc.parse(s, lexer=Lexer()) diff --git a/tests/test_parser.py b/tests/test_parser.py old mode 100644 new mode 100755 index 56e3b71..887540b --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -7,6 +7,7 @@ from os.path import join, dirname import hcl import json +from collections import OrderedDict import pytest @@ -64,28 +65,43 @@ @pytest.mark.parametrize("hcl_fname,invalid", PARSE_FIXTURES) def test_parser_bytes(hcl_fname, invalid): - + with open(join(PARSE_FIXTURE_DIR, hcl_fname), 'rb') as fp: - + input = fp.read() print(input) - + if not invalid: hcl.loads(input) else: with pytest.raises(ValueError): hcl.loads(input) - + @pytest.mark.parametrize("hcl_fname,invalid", PARSE_FIXTURES) def test_parser_str(hcl_fname, invalid): - + with open(join(PARSE_FIXTURE_DIR, hcl_fname), 'r') as fp: - + input = fp.read() print(input) - + if not invalid: hcl.loads(input) else: with pytest.raises(ValueError): hcl.loads(input) + +@pytest.mark.parametrize("hcl_fname,invalid", PARSE_FIXTURES) +def test_parser_object_pairs_hook(hcl_fname, invalid): + with open(join(PARSE_FIXTURE_DIR, hcl_fname), 'r') as fp: + + input = fp.read() + print(input) + + if not invalid: + output = hcl.loads(input, object_pairs_hook=OrderedDict) + dummy = OrderedDict([]) + assert type(output) == type(dummy) + else: + with pytest.raises(ValueError): + hcl.loads(input)