| 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 |