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