1# -*- coding: utf-8 -*- 2from icalendar.compat import iteritems 3from icalendar.parser_tools import to_unicode 4 5from collections import OrderedDict 6 7 8def canonsort_keys(keys, canonical_order=None): 9 """Sorts leading keys according to canonical_order. Keys not specified in 10 canonical_order will appear alphabetically at the end. 11 """ 12 canonical_map = {k: i for i, k in enumerate(canonical_order or [])} 13 head = [k for k in keys if k in canonical_map] 14 tail = [k for k in keys if k not in canonical_map] 15 return sorted(head, key=lambda k: canonical_map[k]) + sorted(tail) 16 17 18def canonsort_items(dict1, canonical_order=None): 19 """Returns a list of items from dict1, sorted by canonical_order. 20 """ 21 return [(k, dict1[k]) for k 22 in canonsort_keys(dict1.keys(), canonical_order)] 23 24 25class CaselessDict(OrderedDict): 26 """A dictionary that isn't case sensitive, and only uses strings as keys. 27 Values retain their case. 28 """ 29 30 def __init__(self, *args, **kwargs): 31 """Set keys to upper for initial dict. 32 """ 33 super(CaselessDict, self).__init__(*args, **kwargs) 34 for key, value in self.items(): 35 key_upper = to_unicode(key).upper() 36 if key != key_upper: 37 super(CaselessDict, self).__delitem__(key) 38 self[key_upper] = value 39 40 def __getitem__(self, key): 41 key = to_unicode(key) 42 return super(CaselessDict, self).__getitem__(key.upper()) 43 44 def __setitem__(self, key, value): 45 key = to_unicode(key) 46 super(CaselessDict, self).__setitem__(key.upper(), value) 47 48 def __delitem__(self, key): 49 key = to_unicode(key) 50 super(CaselessDict, self).__delitem__(key.upper()) 51 52 def __contains__(self, key): 53 key = to_unicode(key) 54 return super(CaselessDict, self).__contains__(key.upper()) 55 56 def get(self, key, default=None): 57 key = to_unicode(key) 58 return super(CaselessDict, self).get(key.upper(), default) 59 60 def setdefault(self, key, value=None): 61 key = to_unicode(key) 62 return super(CaselessDict, self).setdefault(key.upper(), value) 63 64 def pop(self, key, default=None): 65 key = to_unicode(key) 66 return super(CaselessDict, self).pop(key.upper(), default) 67 68 def popitem(self): 69 return super(CaselessDict, self).popitem() 70 71 def has_key(self, key): 72 key = to_unicode(key) 73 return super(CaselessDict, self).__contains__(key.upper()) 74 75 def update(self, *args, **kwargs): 76 # Multiple keys where key1.upper() == key2.upper() will be lost. 77 mappings = list(args) + [kwargs] 78 for mapping in mappings: 79 if hasattr(mapping, 'items'): 80 mapping = iteritems(mapping) 81 for key, value in mapping: 82 self[key] = value 83 84 def copy(self): 85 return type(self)(super(CaselessDict, self).copy()) 86 87 def __repr__(self): 88 return '%s(%s)' % (type(self).__name__, dict(self)) 89 90 def __eq__(self, other): 91 return self is other or dict(self.items()) == dict(other.items()) 92 93 # A list of keys that must appear first in sorted_keys and sorted_items; 94 # must be uppercase. 95 canonical_order = None 96 97 def sorted_keys(self): 98 """Sorts keys according to the canonical_order for the derived class. 99 Keys not specified in canonical_order will appear at the end. 100 """ 101 return canonsort_keys(self.keys(), self.canonical_order) 102 103 def sorted_items(self): 104 """Sorts items according to the canonical_order for the derived class. 105 Items not specified in canonical_order will appear at the end. 106 """ 107 return canonsort_items(self, self.canonical_order) 108