this repo has no description
1import yaml 2 3try: 4 from yaml import CLoader as Loader, CDumper as Dumper 5except ImportError: 6 from yaml import Loader, Dumper # type: ignore 7 8import datetime 9 10 11class Undefined: 12 """ 13 Represents missing values in YAML (as opposed to null which is None in Python) 14 """ 15 16 pass 17 18 19def mapping(tag): 20 """ 21 A decorator which allows for serializing/deserializing a class as a YAML mapping (dictionary). 22 The class must be able to be constructed with `**kwargs` to set its data. 23 24 :param tag: The tag to use for the type (e.g. `!MyTag`) 25 :type tag: str 26 """ 27 28 def decorator(obj_class): 29 def construct(loader, node): 30 try: 31 attrs = loader.construct_mapping(node) 32 instance = obj_class(**attrs) 33 return instance 34 except yaml.constructor.ConstructorError: 35 return obj_class() 36 37 def represent(dumper, data): 38 attrs = { 39 key: value 40 for key, value in data.__dict__.items() 41 if value is not Undefined 42 } 43 return dumper.represent_mapping(tag, attrs) 44 45 yaml.add_constructor(tag, construct, Loader=Loader) 46 yaml.add_representer(obj_class, represent, Dumper=Dumper) 47 return obj_class 48 49 return decorator 50 51 52def sequence(tag): 53 """ 54 A decorator which allows for serializing/deserializing a class as a YAML sequence (list). 55 The class must be able to be constructed with `*args` to set it the items, and also be iterable. 56 57 :param tag: The tag to use for the type (e.g. `!MyTag`) 58 :type tag: str 59 """ 60 61 def decorator(obj_class): 62 def construct(loader, node): 63 try: 64 args = loader.construct_sequence(node) 65 return obj_class(*args) 66 except yaml.constructor.ConstructorError: 67 return obj_class() 68 69 def represent(dumper, data): 70 return dumper.represent_sequence(tag, iter(data)) 71 72 yaml.add_constructor(tag, construct, Loader=Loader) 73 yaml.add_representer(obj_class, represent, Dumper=Dumper) 74 return obj_class 75 76 return decorator 77 78 79def scalar(tag): 80 """ 81 A decorator which allows for serializing/deserializing a class as a YAML scalar (string). 82 The class must be able to be constructed with a string argument, and implement `get_value()` 83 to return the string to be serialized. 84 85 :param tag: The tag to use for the type (e.g. `!MyTag`) 86 :type tag: str 87 """ 88 89 def decorator(obj_class): 90 def construct(loader, node): 91 value = loader.construct_scalar(node) 92 return obj_class(value) 93 94 def represent(dumper, data): 95 return dumper.represent_scalar(tag, str(data.get_value())) 96 97 yaml.add_constructor(tag, construct, Loader=Loader) 98 yaml.add_representer(obj_class, represent, Dumper=Dumper) 99 return obj_class 100 101 return decorator 102 103 104def load(stream): 105 """ 106 Helper function which loads YAML 107 """ 108 return yaml.load(stream, Loader=Loader) 109 110 111def load_all(stream): 112 """ 113 Helper function which loads YAML as a list of documents 114 """ 115 return yaml.load_all(stream, Loader=Loader) 116 117 118def dump(data): 119 """ 120 Helper function which serializes objects as YAML 121 """ 122 return yaml.dump(data, Dumper=Dumper) 123 124 125def dump_all(data): 126 """ 127 Helper function which serializes a list of objects as YAML 128 """ 129 return yaml.dump_all(data, Dumper=Dumper) 130 131 132def range_representer(dumper, data): 133 """ 134 A YAML `!Range l..u` tag 135 """ 136 scalar = u"{}..{}".format(data.start, data.stop - 1) 137 return dumper.represent_scalar(u"!Range", scalar) 138 139 140yaml.add_representer(range, range_representer, Dumper=Dumper) 141 142 143def range_constructor(loader, node): 144 """ 145 A YAML `!Range l..u` tag 146 """ 147 value = loader.construct_scalar(node) 148 a, b = map(int, value.split("..")) 149 return range(a, b + 1) 150 151 152yaml.add_constructor(u"!Range", range_constructor, Loader=Loader) 153 154 155def dt_representer(dumper, data): 156 """ 157 A YAML `!Duration` tag 158 """ 159 scalar = u"{}ms".format(data.total_seconds() * 1000) 160 return dumper.represent_scalar(u"!Duration", scalar) 161 162 163yaml.add_representer(datetime.timedelta, dt_representer, Dumper=Dumper) 164 165 166def dt_constructor(loader, node): 167 """ 168 A YAML `!Duration` tag 169 """ 170 value = loader.construct_scalar(node) 171 if value.endswith("us"): 172 return datetime.timedelta(microseconds=float(value[:-2])) 173 if value.endswith("ms"): 174 return datetime.timedelta(milliseconds=float(value[:-2])) 175 if value.endswith("s"): 176 return datetime.timedelta(seconds=float(value[:-1])) 177 178 179yaml.add_constructor(u"!Duration", dt_constructor, Loader=Loader) 180 181 182def list_representer(dumper, data): 183 """ 184 Changes flow style of lists to be on a single line if only primitives are contained 185 """ 186 flow_style = all(isinstance(i, (int, float, str, bool)) for i in data) 187 return dumper.represent_sequence( 188 "tag:yaml.org,2002:seq", data, flow_style=flow_style 189 ) 190 191 192yaml.add_representer(list, list_representer)