OLD | NEW |
| (Empty) |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import difflib | |
6 import json | |
7 import os | |
8 import pprint | |
9 | |
10 from collections import OrderedDict | |
11 | |
12 try: | |
13 import yaml # pylint: disable=F0401 | |
14 except ImportError: | |
15 yaml = None | |
16 | |
17 | |
18 NonExistant = object() | |
19 | |
20 | |
21 SUPPORTED_SERIALIZERS = {'json', 'yaml'} | |
22 SERIALIZERS = {} | |
23 | |
24 | |
25 # JSON support | |
26 def re_encode(obj): | |
27 if isinstance(obj, dict): | |
28 return {re_encode(k): re_encode(v) for k, v in obj.iteritems()} | |
29 elif isinstance(obj, list): | |
30 return [re_encode(i) for i in obj] | |
31 elif isinstance(obj, unicode): | |
32 return obj.encode('utf-8') | |
33 else: | |
34 return obj | |
35 | |
36 | |
37 SERIALIZERS['json'] = ( | |
38 lambda s: re_encode(json.load(s)), | |
39 lambda data, stream: json.dump( | |
40 data, stream, sort_keys=True, indent=2, separators=(',', ': '))) | |
41 | |
42 | |
43 # YAML support | |
44 if yaml: | |
45 _YAMLSafeLoader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader) | |
46 _YAMLSafeDumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper) | |
47 | |
48 MAPPING_TAG = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG | |
49 | |
50 class OrderedLoader(_YAMLSafeLoader): | |
51 def __init__(self, *args, **kwargs): # pylint: disable=E1002 | |
52 super(OrderedLoader, self).__init__(*args, **kwargs) | |
53 self.add_constructor( | |
54 MAPPING_TAG, | |
55 lambda loader, node: OrderedDict(loader.construct_pairs(node))) | |
56 | |
57 class OrderedDumper(_YAMLSafeDumper): | |
58 def __init__(self, *args, **kwargs): # pylint: disable=E1002 | |
59 super(OrderedDumper, self).__init__(*args, **kwargs) | |
60 def _dict_representer(dumper, data): | |
61 return dumper.represent_mapping(MAPPING_TAG, data.items()) | |
62 self.add_representer(OrderedDict, _dict_representer) | |
63 | |
64 SERIALIZERS['yaml'] = ( | |
65 lambda stream: yaml.load(stream, OrderedLoader), | |
66 lambda data, stream: yaml.dump( | |
67 data, stream, OrderedDumper, default_flow_style=False, | |
68 encoding='utf-8')) | |
69 | |
70 | |
71 def GetCurrentData(test): | |
72 """ | |
73 @type test: Test() | |
74 @returns: The deserialized data (or NonExistant), and a boolean indicating if | |
75 the current serialized data is in the same format which was | |
76 requested by |test|. | |
77 @rtype: (dict, bool) | |
78 """ | |
79 for ext in sorted(SUPPORTED_SERIALIZERS, key=lambda s: s != test.ext): | |
80 path = test.expect_path(ext) | |
81 if path is None: | |
82 return None, True | |
83 | |
84 if ext not in SERIALIZERS and ext == test.ext: | |
85 raise Exception('The package to support %s is not installed.' % ext) | |
86 if os.path.exists(path): | |
87 try: | |
88 with open(path, 'rb') as f: | |
89 data = SERIALIZERS[ext][0](f) | |
90 except ValueError as err: | |
91 raise ValueError('Bad format of %s: %s' % (path, err)) | |
92 return data, ext == test.ext | |
93 return NonExistant, True | |
94 | |
95 | |
96 def WriteNewData(test, data): | |
97 """ | |
98 @type test: Test() | |
99 """ | |
100 if data is None: | |
101 return | |
102 if test.ext not in SUPPORTED_SERIALIZERS: | |
103 raise Exception('%s is not a supported serializer.' % test.ext) | |
104 if test.ext not in SERIALIZERS: | |
105 raise Exception('The package to support %s is not installed.' % test.ext) | |
106 with open(test.expect_path(), 'wb') as f: | |
107 SERIALIZERS[test.ext][1](data, f) | |
108 | |
109 | |
110 def DiffData(old, new): | |
111 """ | |
112 Takes old data and new data, then returns a textual diff as a list of lines. | |
113 @type old: dict | |
114 @type new: dict | |
115 @rtype: [str] | |
116 """ | |
117 if old is NonExistant: | |
118 return new | |
119 if old == new: | |
120 return None | |
121 else: | |
122 return list(difflib.context_diff( | |
123 pprint.pformat(old).splitlines(), | |
124 pprint.pformat(new).splitlines(), | |
125 fromfile='expected', tofile='current', | |
126 n=4, lineterm='' | |
127 )) | |
OLD | NEW |