comparison env/lib/python3.9/site-packages/humanfriendly/case.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # Human friendly input/output in Python.
2 #
3 # Author: Peter Odding <peter@peterodding.com>
4 # Last Change: April 19, 2020
5 # URL: https://humanfriendly.readthedocs.io
6
7 """
8 Simple case insensitive dictionaries.
9
10 The :class:`CaseInsensitiveDict` class is a dictionary whose string keys
11 are case insensitive. It works by automatically coercing string keys to
12 :class:`CaseInsensitiveKey` objects. Keys that are not strings are
13 supported as well, just without case insensitivity.
14
15 At its core this module works by normalizing strings to lowercase before
16 comparing or hashing them. It doesn't support proper case folding nor
17 does it support Unicode normalization, hence the word "simple".
18 """
19
20 # Standard library modules.
21 import collections
22
23 try:
24 # Python >= 3.3.
25 from collections.abc import Iterable, Mapping
26 except ImportError:
27 # Python 2.7.
28 from collections import Iterable, Mapping
29
30 # Modules included in our package.
31 from humanfriendly.compat import basestring, unicode
32
33 # Public identifiers that require documentation.
34 __all__ = ("CaseInsensitiveDict", "CaseInsensitiveKey")
35
36
37 class CaseInsensitiveDict(collections.OrderedDict):
38
39 """
40 Simple case insensitive dictionary implementation (that remembers insertion order).
41
42 This class works by overriding methods that deal with dictionary keys to
43 coerce string keys to :class:`CaseInsensitiveKey` objects before calling
44 down to the regular dictionary handling methods. While intended to be
45 complete this class has not been extensively tested yet.
46 """
47
48 def __init__(self, other=None, **kw):
49 """Initialize a :class:`CaseInsensitiveDict` object."""
50 # Initialize our superclass.
51 super(CaseInsensitiveDict, self).__init__()
52 # Handle the initializer arguments.
53 self.update(other, **kw)
54
55 def coerce_key(self, key):
56 """
57 Coerce string keys to :class:`CaseInsensitiveKey` objects.
58
59 :param key: The value to coerce (any type).
60 :returns: If `key` is a string then a :class:`CaseInsensitiveKey`
61 object is returned, otherwise the value of `key` is
62 returned unmodified.
63 """
64 if isinstance(key, basestring):
65 key = CaseInsensitiveKey(key)
66 return key
67
68 @classmethod
69 def fromkeys(cls, iterable, value=None):
70 """Create a case insensitive dictionary with keys from `iterable` and values set to `value`."""
71 return cls((k, value) for k in iterable)
72
73 def get(self, key, default=None):
74 """Get the value of an existing item."""
75 return super(CaseInsensitiveDict, self).get(self.coerce_key(key), default)
76
77 def pop(self, key, default=None):
78 """Remove an item from a case insensitive dictionary."""
79 return super(CaseInsensitiveDict, self).pop(self.coerce_key(key), default)
80
81 def setdefault(self, key, default=None):
82 """Get the value of an existing item or add a new item."""
83 return super(CaseInsensitiveDict, self).setdefault(self.coerce_key(key), default)
84
85 def update(self, other=None, **kw):
86 """Update a case insensitive dictionary with new items."""
87 if isinstance(other, Mapping):
88 # Copy the items from the given mapping.
89 for key, value in other.items():
90 self[key] = value
91 elif isinstance(other, Iterable):
92 # Copy the items from the given iterable.
93 for key, value in other:
94 self[key] = value
95 elif other is not None:
96 # Complain about unsupported values.
97 msg = "'%s' object is not iterable"
98 type_name = type(value).__name__
99 raise TypeError(msg % type_name)
100 # Copy the keyword arguments (if any).
101 for key, value in kw.items():
102 self[key] = value
103
104 def __contains__(self, key):
105 """Check if a case insensitive dictionary contains the given key."""
106 return super(CaseInsensitiveDict, self).__contains__(self.coerce_key(key))
107
108 def __delitem__(self, key):
109 """Delete an item in a case insensitive dictionary."""
110 return super(CaseInsensitiveDict, self).__delitem__(self.coerce_key(key))
111
112 def __getitem__(self, key):
113 """Get the value of an item in a case insensitive dictionary."""
114 return super(CaseInsensitiveDict, self).__getitem__(self.coerce_key(key))
115
116 def __setitem__(self, key, value):
117 """Set the value of an item in a case insensitive dictionary."""
118 return super(CaseInsensitiveDict, self).__setitem__(self.coerce_key(key), value)
119
120
121 class CaseInsensitiveKey(unicode):
122
123 """
124 Simple case insensitive dictionary key implementation.
125
126 The :class:`CaseInsensitiveKey` class provides an intentionally simple
127 implementation of case insensitive strings to be used as dictionary keys.
128
129 If you need features like Unicode normalization or proper case folding
130 please consider using a more advanced implementation like the :pypi:`istr`
131 package instead.
132 """
133
134 def __new__(cls, value):
135 """Create a :class:`CaseInsensitiveKey` object."""
136 # Delegate string object creation to our superclass.
137 obj = unicode.__new__(cls, value)
138 # Store the lowercased string and its hash value.
139 normalized = obj.lower()
140 obj._normalized = normalized
141 obj._hash_value = hash(normalized)
142 return obj
143
144 def __hash__(self):
145 """Get the hash value of the lowercased string."""
146 return self._hash_value
147
148 def __eq__(self, other):
149 """Compare two strings as lowercase."""
150 if isinstance(other, CaseInsensitiveKey):
151 # Fast path (and the most common case): Comparison with same type.
152 return self._normalized == other._normalized
153 elif isinstance(other, unicode):
154 # Slow path: Comparison with strings that need lowercasing.
155 return self._normalized == other.lower()
156 else:
157 return NotImplemented