We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove Python `collections` Deprecation warnings
* Python: 3.7 and below

### What was wrong?
Python 3.8 is changing the way imports from `collections` are being handled. The following Deprecation warning describes the issue: 
`DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working`

### How can it be fixed?
Go through the codebase and swap out any `collections` imports for `collections.abc`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `web3/_utils/formatters.py`
Content:
```
1 from collections import (
2     Iterable,
3     Mapping,
4 )
5 
6 from eth_utils import (
7     is_dict,
8     is_list_like,
9     is_string,
10     to_dict,
11     to_list,
12 )
13 
14 from web3._utils.decorators import (
15     reject_recursive_repeats,
16 )
17 from web3._utils.toolz import (
18     compose,
19     curry,
20     dissoc,
21 )
22 
23 
24 def hex_to_integer(value):
25     return int(value, 16)
26 
27 
28 integer_to_hex = hex
29 
30 
31 @curry
32 @to_list
33 def apply_formatter_at_index(formatter, at_index, value):
34     if at_index + 1 > len(value):
35         raise IndexError(
36             "Not enough values in iterable to apply formatter.  Got: {0}. "
37             "Need: {1}".format(len(value), at_index + 1)
38         )
39     for index, item in enumerate(value):
40         if index == at_index:
41             yield formatter(item)
42         else:
43             yield item
44 
45 
46 def apply_formatters_to_args(*formatters):
47     return compose(*(
48         apply_formatter_at_index(formatter, index)
49         for index, formatter
50         in enumerate(formatters)
51     ))
52 
53 
54 @curry
55 def apply_formatter_if(condition, formatter, value):
56     if condition(value):
57         return formatter(value)
58     else:
59         return value
60 
61 
62 @curry
63 @to_dict
64 def apply_formatters_to_dict(formatters, value):
65     for key, item in value.items():
66         if key in formatters:
67             try:
68                 yield key, formatters[key](item)
69             except (TypeError, ValueError) as exc:
70                 raise type(exc)("Could not format value %r as field %r" % (item, key)) from exc
71         else:
72             yield key, item
73 
74 
75 @curry
76 @to_list
77 def apply_formatter_to_array(formatter, value):
78     for item in value:
79         yield formatter(item)
80 
81 
82 @curry
83 def apply_one_of_formatters(formatter_condition_pairs, value):
84     for formatter, condition in formatter_condition_pairs:
85         if condition(value):
86             return formatter(value)
87     else:
88         raise ValueError("The provided value did not satisfy any of the formatter conditions")
89 
90 
91 def map_collection(func, collection):
92     """
93     Apply func to each element of a collection, or value of a dictionary.
94     If the value is not a collection, return it unmodified
95     """
96     datatype = type(collection)
97     if isinstance(collection, Mapping):
98         return datatype((key, func(val)) for key, val in collection.items())
99     if is_string(collection):
100         return collection
101     elif isinstance(collection, Iterable):
102         return datatype(map(func, collection))
103     else:
104         return collection
105 
106 
107 @reject_recursive_repeats
108 def recursive_map(func, data):
109     """
110     Apply func to data, and any collection items inside data (using map_collection).
111     Define func so that it only applies to the type of value that you want it to apply to.
112     """
113     def recurse(item):
114         return recursive_map(func, item)
115     items_mapped = map_collection(recurse, data)
116     return func(items_mapped)
117 
118 
119 def static_return(value):
120     def inner(*args, **kwargs):
121         return value
122     return inner
123 
124 
125 def static_result(value):
126     def inner(*args, **kwargs):
127         return {'result': value}
128     return inner
129 
130 
131 @curry
132 @to_dict
133 def apply_key_map(key_mappings, value):
134     for key, item in value.items():
135         if key in key_mappings:
136             yield key_mappings[key], item
137         else:
138             yield key, item
139 
140 
141 def is_array_of_strings(value):
142     if not is_list_like(value):
143         return False
144     return all((is_string(item) for item in value))
145 
146 
147 def is_array_of_dicts(value):
148     if not is_list_like(value):
149         return False
150     return all((is_dict(item) for item in value))
151 
152 
153 @curry
154 def remove_key_if(key, remove_if, input_dict):
155     if key in input_dict and remove_if(input_dict):
156         return dissoc(input_dict, key)
157     else:
158         return input_dict
159
```

Path: `web3/datastructures.py`
Content:
```
1 from collections import (
2     Hashable,
3     Mapping,
4     MutableMapping,
5     OrderedDict,
6     Sequence,
7 )
8 
9 from eth_utils import (
10     is_integer,
11 )
12 
13 from web3._utils.formatters import (
14     recursive_map,
15 )
16 
17 # Hashable must be immutable:
18 # "the implementation of hashable collections requires that a key's hash value is immutable"
19 # https://docs.python.org/3/reference/datamodel.html#object.__hash__
20 
21 
22 class ReadableAttributeDict(Mapping):
23     """
24     The read attributes for the AttributeDict types
25     """
26 
27     def __init__(self, dictionary, *args, **kwargs):
28         self.__dict__ = dict(dictionary)
29         self.__dict__.update(dict(*args, **kwargs))
30 
31     def __getitem__(self, key):
32         return self.__dict__[key]
33 
34     def __iter__(self):
35         return iter(self.__dict__)
36 
37     def __len__(self):
38         return len(self.__dict__)
39 
40     def __repr__(self):
41         return self.__class__.__name__ + "(%r)" % self.__dict__
42 
43     def _repr_pretty_(self, builder, cycle):
44         """
45         Custom pretty output for the IPython console
46         """
47         builder.text(self.__class__.__name__ + "(")
48         if cycle:
49             builder.text("<cycle>")
50         else:
51             builder.pretty(self.__dict__)
52         builder.text(")")
53 
54     @classmethod
55     def _apply_if_mapping(cls, value):
56         if isinstance(value, Mapping):
57             return cls(value)
58         else:
59             return value
60 
61     @classmethod
62     def recursive(cls, value):
63         return recursive_map(cls._apply_if_mapping, value)
64 
65 
66 class MutableAttributeDict(MutableMapping, ReadableAttributeDict):
67 
68     def __setitem__(self, key, val):
69         self.__dict__[key] = val
70 
71     def __delitem__(self, key):
72         del self.__dict__[key]
73 
74 
75 class AttributeDict(ReadableAttributeDict, Hashable):
76     """
77     This provides superficial immutability, someone could hack around it
78     """
79 
80     def __setattr__(self, attr, val):
81         if attr == '__dict__':
82             super().__setattr__(attr, val)
83         else:
84             raise TypeError('This data is immutable -- create a copy instead of modifying')
85 
86     def __delattr__(self, key):
87         raise TypeError('This data is immutable -- create a copy instead of modifying')
88 
89     def __hash__(self):
90         return hash(tuple(sorted(self.items())))
91 
92     def __eq__(self, other):
93         if isinstance(other, Mapping):
94             return self.__dict__ == dict(other)
95         else:
96             return False
97 
98 
99 class NamedElementOnion(Mapping):
100     """
101     Add layers to an onion-shaped structure. Optionally, inject to a specific layer.
102     This structure is iterable, where the outermost layer is first, and innermost is last.
103     """
104 
105     def __init__(self, init_elements, valid_element=callable):
106         self._queue = OrderedDict()
107         for element in reversed(init_elements):
108             if valid_element(element):
109                 self.add(element)
110             else:
111                 self.add(*element)
112 
113     def add(self, element, name=None):
114         if name is None:
115             name = element
116 
117         if name in self._queue:
118             if name is element:
119                 raise ValueError("You can't add the same un-named instance twice")
120             else:
121                 raise ValueError("You can't add the same name again, use replace instead")
122 
123         self._queue[name] = element
124 
125     def inject(self, element, name=None, layer=None):
126         """
127         Inject a named element to an arbitrary layer in the onion.
128 
129         The current implementation only supports insertion at the innermost layer,
130         or at the outermost layer. Note that inserting to the outermost is equivalent
131         to calling :meth:`add` .
132         """
133         if not is_integer(layer):
134             raise TypeError("The layer for insertion must be an int.")
135         elif layer != 0 and layer != len(self._queue):
136             raise NotImplementedError(
137                 "You can only insert to the beginning or end of a %s, currently. "
138                 "You tried to insert to %d, but only 0 and %d are permitted. " % (
139                     type(self),
140                     layer,
141                     len(self._queue),
142                 )
143             )
144 
145         self.add(element, name=name)
146 
147         if layer == 0:
148             if name is None:
149                 name = element
150             self._queue.move_to_end(name, last=False)
151         elif layer == len(self._queue):
152             return
153         else:
154             raise AssertionError("Impossible to reach: earlier validation raises an error")
155 
156     def clear(self):
157         self._queue.clear()
158 
159     def replace(self, old, new):
160         if old not in self._queue:
161             raise ValueError("You can't replace unless one already exists, use add instead")
162         to_be_replaced = self._queue[old]
163         if to_be_replaced is old:
164             # re-insert with new name in old slot
165             self._replace_with_new_name(old, new)
166         else:
167             self._queue[old] = new
168         return to_be_replaced
169 
170     def remove(self, old):
171         if old not in self._queue:
172             raise ValueError("You can only remove something that has been added")
173         del self._queue[old]
174 
175     def _replace_with_new_name(self, old, new):
176         self._queue[new] = new
177         found_old = False
178         for key in list(self._queue.keys()):
179             if not found_old:
180                 if key == old:
181                     found_old = True
182                 continue
183             elif key != new:
184                 self._queue.move_to_end(key)
185         del self._queue[old]
186 
187     def __iter__(self):
188         elements = self._queue.values()
189         if not isinstance(elements, Sequence):
190             elements = list(elements)
191         return iter(reversed(elements))
192 
193     def __add__(self, other):
194         if not isinstance(other, NamedElementOnion):
195             raise NotImplementedError("You can only combine with another NamedElementOnion")
196         combined = self._queue.copy()
197         combined.update(other._queue)
198         return NamedElementOnion(combined.items())
199 
200     def __contains__(self, element):
201         return element in self._queue
202 
203     def __getitem__(self, element):
204         return self._queue[element]
205 
206     def __len__(self):
207         return len(self._queue)
208 
209     def __reversed__(self):
210         elements = self._queue.values()
211         if not isinstance(elements, Sequence):
212             elements = list(elements)
213         return iter(elements)
214
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/web3/_utils/formatters.py b/web3/_utils/formatters.py
--- a/web3/_utils/formatters.py
+++ b/web3/_utils/formatters.py
@@ -1,4 +1,4 @@
-from collections import (
+from collections.abc import (
     Iterable,
     Mapping,
 )
diff --git a/web3/datastructures.py b/web3/datastructures.py
--- a/web3/datastructures.py
+++ b/web3/datastructures.py
@@ -1,8 +1,10 @@
 from collections import (
+    OrderedDict,
+)
+from collections.abc import (
     Hashable,
     Mapping,
     MutableMapping,
-    OrderedDict,
     Sequence,
 )

{"golden_diff": "diff --git a/web3/_utils/formatters.py b/web3/_utils/formatters.py\n--- a/web3/_utils/formatters.py\n+++ b/web3/_utils/formatters.py\n@@ -1,4 +1,4 @@\n-from collections import (\n+from collections.abc import (\n     Iterable,\n     Mapping,\n )\ndiff --git a/web3/datastructures.py b/web3/datastructures.py\n--- a/web3/datastructures.py\n+++ b/web3/datastructures.py\n@@ -1,8 +1,10 @@\n from collections import (\n+    OrderedDict,\n+)\n+from collections.abc import (\n     Hashable,\n     Mapping,\n     MutableMapping,\n-    OrderedDict,\n     Sequence,\n )\n", "issue": "Remove Python `collections` Deprecation warnings\n* Python: 3.7 and below\r\n\r\n### What was wrong?\r\nPython 3.8 is changing the way imports from `collections` are being handled. The following Deprecation warning describes the issue: \r\n`DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working`\r\n\r\n### How can it be fixed?\r\nGo through the codebase and swap out any `collections` imports for `collections.abc`\n", "before_files": [{"content": "from collections import (\n    Iterable,\n    Mapping,\n)\n\nfrom eth_utils import (\n    is_dict,\n    is_list_like,\n    is_string,\n    to_dict,\n    to_list,\n)\n\nfrom web3._utils.decorators import (\n    reject_recursive_repeats,\n)\nfrom web3._utils.toolz import (\n    compose,\n    curry,\n    dissoc,\n)\n\n\ndef hex_to_integer(value):\n    return int(value, 16)\n\n\ninteger_to_hex = hex\n\n\n@curry\n@to_list\ndef apply_formatter_at_index(formatter, at_index, value):\n    if at_index + 1 > len(value):\n        raise IndexError(\n            \"Not enough values in iterable to apply formatter.  Got: {0}. \"\n            \"Need: {1}\".format(len(value), at_index + 1)\n        )\n    for index, item in enumerate(value):\n        if index == at_index:\n            yield formatter(item)\n        else:\n            yield item\n\n\ndef apply_formatters_to_args(*formatters):\n    return compose(*(\n        apply_formatter_at_index(formatter, index)\n        for index, formatter\n        in enumerate(formatters)\n    ))\n\n\n@curry\ndef apply_formatter_if(condition, formatter, value):\n    if condition(value):\n        return formatter(value)\n    else:\n        return value\n\n\n@curry\n@to_dict\ndef apply_formatters_to_dict(formatters, value):\n    for key, item in value.items():\n        if key in formatters:\n            try:\n                yield key, formatters[key](item)\n            except (TypeError, ValueError) as exc:\n                raise type(exc)(\"Could not format value %r as field %r\" % (item, key)) from exc\n        else:\n            yield key, item\n\n\n@curry\n@to_list\ndef apply_formatter_to_array(formatter, value):\n    for item in value:\n        yield formatter(item)\n\n\n@curry\ndef apply_one_of_formatters(formatter_condition_pairs, value):\n    for formatter, condition in formatter_condition_pairs:\n        if condition(value):\n            return formatter(value)\n    else:\n        raise ValueError(\"The provided value did not satisfy any of the formatter conditions\")\n\n\ndef map_collection(func, collection):\n    \"\"\"\n    Apply func to each element of a collection, or value of a dictionary.\n    If the value is not a collection, return it unmodified\n    \"\"\"\n    datatype = type(collection)\n    if isinstance(collection, Mapping):\n        return datatype((key, func(val)) for key, val in collection.items())\n    if is_string(collection):\n        return collection\n    elif isinstance(collection, Iterable):\n        return datatype(map(func, collection))\n    else:\n        return collection\n\n\n@reject_recursive_repeats\ndef recursive_map(func, data):\n    \"\"\"\n    Apply func to data, and any collection items inside data (using map_collection).\n    Define func so that it only applies to the type of value that you want it to apply to.\n    \"\"\"\n    def recurse(item):\n        return recursive_map(func, item)\n    items_mapped = map_collection(recurse, data)\n    return func(items_mapped)\n\n\ndef static_return(value):\n    def inner(*args, **kwargs):\n        return value\n    return inner\n\n\ndef static_result(value):\n    def inner(*args, **kwargs):\n        return {'result': value}\n    return inner\n\n\n@curry\n@to_dict\ndef apply_key_map(key_mappings, value):\n    for key, item in value.items():\n        if key in key_mappings:\n            yield key_mappings[key], item\n        else:\n            yield key, item\n\n\ndef is_array_of_strings(value):\n    if not is_list_like(value):\n        return False\n    return all((is_string(item) for item in value))\n\n\ndef is_array_of_dicts(value):\n    if not is_list_like(value):\n        return False\n    return all((is_dict(item) for item in value))\n\n\n@curry\ndef remove_key_if(key, remove_if, input_dict):\n    if key in input_dict and remove_if(input_dict):\n        return dissoc(input_dict, key)\n    else:\n        return input_dict\n", "path": "web3/_utils/formatters.py"}, {"content": "from collections import (\n    Hashable,\n    Mapping,\n    MutableMapping,\n    OrderedDict,\n    Sequence,\n)\n\nfrom eth_utils import (\n    is_integer,\n)\n\nfrom web3._utils.formatters import (\n    recursive_map,\n)\n\n# Hashable must be immutable:\n# \"the implementation of hashable collections requires that a key's hash value is immutable\"\n# https://docs.python.org/3/reference/datamodel.html#object.__hash__\n\n\nclass ReadableAttributeDict(Mapping):\n    \"\"\"\n    The read attributes for the AttributeDict types\n    \"\"\"\n\n    def __init__(self, dictionary, *args, **kwargs):\n        self.__dict__ = dict(dictionary)\n        self.__dict__.update(dict(*args, **kwargs))\n\n    def __getitem__(self, key):\n        return self.__dict__[key]\n\n    def __iter__(self):\n        return iter(self.__dict__)\n\n    def __len__(self):\n        return len(self.__dict__)\n\n    def __repr__(self):\n        return self.__class__.__name__ + \"(%r)\" % self.__dict__\n\n    def _repr_pretty_(self, builder, cycle):\n        \"\"\"\n        Custom pretty output for the IPython console\n        \"\"\"\n        builder.text(self.__class__.__name__ + \"(\")\n        if cycle:\n            builder.text(\"<cycle>\")\n        else:\n            builder.pretty(self.__dict__)\n        builder.text(\")\")\n\n    @classmethod\n    def _apply_if_mapping(cls, value):\n        if isinstance(value, Mapping):\n            return cls(value)\n        else:\n            return value\n\n    @classmethod\n    def recursive(cls, value):\n        return recursive_map(cls._apply_if_mapping, value)\n\n\nclass MutableAttributeDict(MutableMapping, ReadableAttributeDict):\n\n    def __setitem__(self, key, val):\n        self.__dict__[key] = val\n\n    def __delitem__(self, key):\n        del self.__dict__[key]\n\n\nclass AttributeDict(ReadableAttributeDict, Hashable):\n    \"\"\"\n    This provides superficial immutability, someone could hack around it\n    \"\"\"\n\n    def __setattr__(self, attr, val):\n        if attr == '__dict__':\n            super().__setattr__(attr, val)\n        else:\n            raise TypeError('This data is immutable -- create a copy instead of modifying')\n\n    def __delattr__(self, key):\n        raise TypeError('This data is immutable -- create a copy instead of modifying')\n\n    def __hash__(self):\n        return hash(tuple(sorted(self.items())))\n\n    def __eq__(self, other):\n        if isinstance(other, Mapping):\n            return self.__dict__ == dict(other)\n        else:\n            return False\n\n\nclass NamedElementOnion(Mapping):\n    \"\"\"\n    Add layers to an onion-shaped structure. Optionally, inject to a specific layer.\n    This structure is iterable, where the outermost layer is first, and innermost is last.\n    \"\"\"\n\n    def __init__(self, init_elements, valid_element=callable):\n        self._queue = OrderedDict()\n        for element in reversed(init_elements):\n            if valid_element(element):\n                self.add(element)\n            else:\n                self.add(*element)\n\n    def add(self, element, name=None):\n        if name is None:\n            name = element\n\n        if name in self._queue:\n            if name is element:\n                raise ValueError(\"You can't add the same un-named instance twice\")\n            else:\n                raise ValueError(\"You can't add the same name again, use replace instead\")\n\n        self._queue[name] = element\n\n    def inject(self, element, name=None, layer=None):\n        \"\"\"\n        Inject a named element to an arbitrary layer in the onion.\n\n        The current implementation only supports insertion at the innermost layer,\n        or at the outermost layer. Note that inserting to the outermost is equivalent\n        to calling :meth:`add` .\n        \"\"\"\n        if not is_integer(layer):\n            raise TypeError(\"The layer for insertion must be an int.\")\n        elif layer != 0 and layer != len(self._queue):\n            raise NotImplementedError(\n                \"You can only insert to the beginning or end of a %s, currently. \"\n                \"You tried to insert to %d, but only 0 and %d are permitted. \" % (\n                    type(self),\n                    layer,\n                    len(self._queue),\n                )\n            )\n\n        self.add(element, name=name)\n\n        if layer == 0:\n            if name is None:\n                name = element\n            self._queue.move_to_end(name, last=False)\n        elif layer == len(self._queue):\n            return\n        else:\n            raise AssertionError(\"Impossible to reach: earlier validation raises an error\")\n\n    def clear(self):\n        self._queue.clear()\n\n    def replace(self, old, new):\n        if old not in self._queue:\n            raise ValueError(\"You can't replace unless one already exists, use add instead\")\n        to_be_replaced = self._queue[old]\n        if to_be_replaced is old:\n            # re-insert with new name in old slot\n            self._replace_with_new_name(old, new)\n        else:\n            self._queue[old] = new\n        return to_be_replaced\n\n    def remove(self, old):\n        if old not in self._queue:\n            raise ValueError(\"You can only remove something that has been added\")\n        del self._queue[old]\n\n    def _replace_with_new_name(self, old, new):\n        self._queue[new] = new\n        found_old = False\n        for key in list(self._queue.keys()):\n            if not found_old:\n                if key == old:\n                    found_old = True\n                continue\n            elif key != new:\n                self._queue.move_to_end(key)\n        del self._queue[old]\n\n    def __iter__(self):\n        elements = self._queue.values()\n        if not isinstance(elements, Sequence):\n            elements = list(elements)\n        return iter(reversed(elements))\n\n    def __add__(self, other):\n        if not isinstance(other, NamedElementOnion):\n            raise NotImplementedError(\"You can only combine with another NamedElementOnion\")\n        combined = self._queue.copy()\n        combined.update(other._queue)\n        return NamedElementOnion(combined.items())\n\n    def __contains__(self, element):\n        return element in self._queue\n\n    def __getitem__(self, element):\n        return self._queue[element]\n\n    def __len__(self):\n        return len(self._queue)\n\n    def __reversed__(self):\n        elements = self._queue.values()\n        if not isinstance(elements, Sequence):\n            elements = list(elements)\n        return iter(elements)\n", "path": "web3/datastructures.py"}], "after_files": [{"content": "from collections.abc import (\n    Iterable,\n    Mapping,\n)\n\nfrom eth_utils import (\n    is_dict,\n    is_list_like,\n    is_string,\n    to_dict,\n    to_list,\n)\n\nfrom web3._utils.decorators import (\n    reject_recursive_repeats,\n)\nfrom web3._utils.toolz import (\n    compose,\n    curry,\n    dissoc,\n)\n\n\ndef hex_to_integer(value):\n    return int(value, 16)\n\n\ninteger_to_hex = hex\n\n\n@curry\n@to_list\ndef apply_formatter_at_index(formatter, at_index, value):\n    if at_index + 1 > len(value):\n        raise IndexError(\n            \"Not enough values in iterable to apply formatter.  Got: {0}. \"\n            \"Need: {1}\".format(len(value), at_index + 1)\n        )\n    for index, item in enumerate(value):\n        if index == at_index:\n            yield formatter(item)\n        else:\n            yield item\n\n\ndef apply_formatters_to_args(*formatters):\n    return compose(*(\n        apply_formatter_at_index(formatter, index)\n        for index, formatter\n        in enumerate(formatters)\n    ))\n\n\n@curry\ndef apply_formatter_if(condition, formatter, value):\n    if condition(value):\n        return formatter(value)\n    else:\n        return value\n\n\n@curry\n@to_dict\ndef apply_formatters_to_dict(formatters, value):\n    for key, item in value.items():\n        if key in formatters:\n            try:\n                yield key, formatters[key](item)\n            except (TypeError, ValueError) as exc:\n                raise type(exc)(\"Could not format value %r as field %r\" % (item, key)) from exc\n        else:\n            yield key, item\n\n\n@curry\n@to_list\ndef apply_formatter_to_array(formatter, value):\n    for item in value:\n        yield formatter(item)\n\n\n@curry\ndef apply_one_of_formatters(formatter_condition_pairs, value):\n    for formatter, condition in formatter_condition_pairs:\n        if condition(value):\n            return formatter(value)\n    else:\n        raise ValueError(\"The provided value did not satisfy any of the formatter conditions\")\n\n\ndef map_collection(func, collection):\n    \"\"\"\n    Apply func to each element of a collection, or value of a dictionary.\n    If the value is not a collection, return it unmodified\n    \"\"\"\n    datatype = type(collection)\n    if isinstance(collection, Mapping):\n        return datatype((key, func(val)) for key, val in collection.items())\n    if is_string(collection):\n        return collection\n    elif isinstance(collection, Iterable):\n        return datatype(map(func, collection))\n    else:\n        return collection\n\n\n@reject_recursive_repeats\ndef recursive_map(func, data):\n    \"\"\"\n    Apply func to data, and any collection items inside data (using map_collection).\n    Define func so that it only applies to the type of value that you want it to apply to.\n    \"\"\"\n    def recurse(item):\n        return recursive_map(func, item)\n    items_mapped = map_collection(recurse, data)\n    return func(items_mapped)\n\n\ndef static_return(value):\n    def inner(*args, **kwargs):\n        return value\n    return inner\n\n\ndef static_result(value):\n    def inner(*args, **kwargs):\n        return {'result': value}\n    return inner\n\n\n@curry\n@to_dict\ndef apply_key_map(key_mappings, value):\n    for key, item in value.items():\n        if key in key_mappings:\n            yield key_mappings[key], item\n        else:\n            yield key, item\n\n\ndef is_array_of_strings(value):\n    if not is_list_like(value):\n        return False\n    return all((is_string(item) for item in value))\n\n\ndef is_array_of_dicts(value):\n    if not is_list_like(value):\n        return False\n    return all((is_dict(item) for item in value))\n\n\n@curry\ndef remove_key_if(key, remove_if, input_dict):\n    if key in input_dict and remove_if(input_dict):\n        return dissoc(input_dict, key)\n    else:\n        return input_dict\n", "path": "web3/_utils/formatters.py"}, {"content": "from collections import (\n    OrderedDict,\n)\nfrom collections.abc import (\n    Hashable,\n    Mapping,\n    MutableMapping,\n    Sequence,\n)\n\nfrom eth_utils import (\n    is_integer,\n)\n\nfrom web3._utils.formatters import (\n    recursive_map,\n)\n\n# Hashable must be immutable:\n# \"the implementation of hashable collections requires that a key's hash value is immutable\"\n# https://docs.python.org/3/reference/datamodel.html#object.__hash__\n\n\nclass ReadableAttributeDict(Mapping):\n    \"\"\"\n    The read attributes for the AttributeDict types\n    \"\"\"\n\n    def __init__(self, dictionary, *args, **kwargs):\n        self.__dict__ = dict(dictionary)\n        self.__dict__.update(dict(*args, **kwargs))\n\n    def __getitem__(self, key):\n        return self.__dict__[key]\n\n    def __iter__(self):\n        return iter(self.__dict__)\n\n    def __len__(self):\n        return len(self.__dict__)\n\n    def __repr__(self):\n        return self.__class__.__name__ + \"(%r)\" % self.__dict__\n\n    def _repr_pretty_(self, builder, cycle):\n        \"\"\"\n        Custom pretty output for the IPython console\n        \"\"\"\n        builder.text(self.__class__.__name__ + \"(\")\n        if cycle:\n            builder.text(\"<cycle>\")\n        else:\n            builder.pretty(self.__dict__)\n        builder.text(\")\")\n\n    @classmethod\n    def _apply_if_mapping(cls, value):\n        if isinstance(value, Mapping):\n            return cls(value)\n        else:\n            return value\n\n    @classmethod\n    def recursive(cls, value):\n        return recursive_map(cls._apply_if_mapping, value)\n\n\nclass MutableAttributeDict(MutableMapping, ReadableAttributeDict):\n\n    def __setitem__(self, key, val):\n        self.__dict__[key] = val\n\n    def __delitem__(self, key):\n        del self.__dict__[key]\n\n\nclass AttributeDict(ReadableAttributeDict, Hashable):\n    \"\"\"\n    This provides superficial immutability, someone could hack around it\n    \"\"\"\n\n    def __setattr__(self, attr, val):\n        if attr == '__dict__':\n            super().__setattr__(attr, val)\n        else:\n            raise TypeError('This data is immutable -- create a copy instead of modifying')\n\n    def __delattr__(self, key):\n        raise TypeError('This data is immutable -- create a copy instead of modifying')\n\n    def __hash__(self):\n        return hash(tuple(sorted(self.items())))\n\n    def __eq__(self, other):\n        if isinstance(other, Mapping):\n            return self.__dict__ == dict(other)\n        else:\n            return False\n\n\nclass NamedElementOnion(Mapping):\n    \"\"\"\n    Add layers to an onion-shaped structure. Optionally, inject to a specific layer.\n    This structure is iterable, where the outermost layer is first, and innermost is last.\n    \"\"\"\n\n    def __init__(self, init_elements, valid_element=callable):\n        self._queue = OrderedDict()\n        for element in reversed(init_elements):\n            if valid_element(element):\n                self.add(element)\n            else:\n                self.add(*element)\n\n    def add(self, element, name=None):\n        if name is None:\n            name = element\n\n        if name in self._queue:\n            if name is element:\n                raise ValueError(\"You can't add the same un-named instance twice\")\n            else:\n                raise ValueError(\"You can't add the same name again, use replace instead\")\n\n        self._queue[name] = element\n\n    def inject(self, element, name=None, layer=None):\n        \"\"\"\n        Inject a named element to an arbitrary layer in the onion.\n\n        The current implementation only supports insertion at the innermost layer,\n        or at the outermost layer. Note that inserting to the outermost is equivalent\n        to calling :meth:`add` .\n        \"\"\"\n        if not is_integer(layer):\n            raise TypeError(\"The layer for insertion must be an int.\")\n        elif layer != 0 and layer != len(self._queue):\n            raise NotImplementedError(\n                \"You can only insert to the beginning or end of a %s, currently. \"\n                \"You tried to insert to %d, but only 0 and %d are permitted. \" % (\n                    type(self),\n                    layer,\n                    len(self._queue),\n                )\n            )\n\n        self.add(element, name=name)\n\n        if layer == 0:\n            if name is None:\n                name = element\n            self._queue.move_to_end(name, last=False)\n        elif layer == len(self._queue):\n            return\n        else:\n            raise AssertionError(\"Impossible to reach: earlier validation raises an error\")\n\n    def clear(self):\n        self._queue.clear()\n\n    def replace(self, old, new):\n        if old not in self._queue:\n            raise ValueError(\"You can't replace unless one already exists, use add instead\")\n        to_be_replaced = self._queue[old]\n        if to_be_replaced is old:\n            # re-insert with new name in old slot\n            self._replace_with_new_name(old, new)\n        else:\n            self._queue[old] = new\n        return to_be_replaced\n\n    def remove(self, old):\n        if old not in self._queue:\n            raise ValueError(\"You can only remove something that has been added\")\n        del self._queue[old]\n\n    def _replace_with_new_name(self, old, new):\n        self._queue[new] = new\n        found_old = False\n        for key in list(self._queue.keys()):\n            if not found_old:\n                if key == old:\n                    found_old = True\n                continue\n            elif key != new:\n                self._queue.move_to_end(key)\n        del self._queue[old]\n\n    def __iter__(self):\n        elements = self._queue.values()\n        if not isinstance(elements, Sequence):\n            elements = list(elements)\n        return iter(reversed(elements))\n\n    def __add__(self, other):\n        if not isinstance(other, NamedElementOnion):\n            raise NotImplementedError(\"You can only combine with another NamedElementOnion\")\n        combined = self._queue.copy()\n        combined.update(other._queue)\n        return NamedElementOnion(combined.items())\n\n    def __contains__(self, element):\n        return element in self._queue\n\n    def __getitem__(self, element):\n        return self._queue[element]\n\n    def __len__(self):\n        return len(self._queue)\n\n    def __reversed__(self):\n        elements = self._queue.values()\n        if not isinstance(elements, Sequence):\n            elements = list(elements)\n        return iter(elements)\n", "path": "web3/datastructures.py"}]}