We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Connection to ropsten websocket over infura hang.
* Version: 4.4.1
* Python: 3.6
* OS: osx


### What was wrong?

I wasn't able to connect to infura's ropsten websocket. The process hung when I wanted to create the filter object on pending transactions. It worked for both rinkeby's and mainnet's but just not for ropsten network.

I was wondering if anyone is having the same issue. Given that I was able to connect to two other networks it makes it less suspicious that the problem is caused by the web3.py

Below is how I instantiate the filter by following https://web3py.readthedocs.io/en/stable/filters.html#web3.utils.filters.TransactionFilter
```python
In [23]: web3 = Web3(Web3.WebsocketProvider('wss://rinkeby.infura.io/_ws'))

In [24]: new_transaction_filter = web3.eth.filter('pending')

In [25]: new_transaction_filter.get_new_entries()
Out[25]:
[HexBytes('0x4945f39cbeedd911eae93577c75d763d364c37ae01dcb7fb31ff9421a2a92b71'),
 HexBytes('0xae0b88b85163d3f0a918236cbf3ec93ccb589c6ad327e937f69b9591680fbf61'),
```

Process hung on line 27 for a long time. I interrupted. The stacktrace is below.
```python
In [26]: web3 = Web3(Web3.WebsocketProvider('wss://ropsten.infura.io/ws'))

In [27]: new_transaction_filter = web3.eth.filter('pending')

^C---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-3-60afc0d44840> in <module>()
----> 1 new_transaction_filter = web3.eth.filter('pending')

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/eth.py in filter(self, filter_params, filter_id)
    319             elif filter_params == "pending":
    320                 filter_id = self.web3.manager.request_blocking(
--> 321                     "eth_newPendingTransactionFilter", [],
    322                 )
    323                 return TransactionFilter(self.web3, filter_id)

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/manager.py in request_blocking(self, method, params)
    105         Make a synchronous request using the provider
    106         """
--> 107         response = self._make_request(method, params)
    108
    109         if "error" in response:

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/manager.py in _make_request(self, method, params)
     88             self.logger.debug("Making request. Method: %s", method)
     89             try:
---> 90                 return request_func(method, params)
     91             except CannotHandleRequest:
     92                 continue

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/gas_price_strategy.py in middleware(method, params)
     16                     transaction = assoc(transaction, 'gasPrice', generated_gas_price)
     17                     return make_request(method, [transaction])
---> 18         return make_request(method, params)
     19     return middleware

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/cytoolz/functoolz.pyx in cytoolz.functoolz.curry.__call__()

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/formatting.py in apply_formatters(method, params, make_request, request_formatters, result_formatters, error_formatters)
     48         response = make_request(method, formatted_params)
     49     else:
---> 50         response = make_request(method, params)
     51
     52     if 'result' in response and method in result_formatters:

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/attrdict.py in middleware(method, params)
     16     """
     17     def middleware(method, params):
---> 18         response = make_request(method, params)
     19
     20         if 'result' in response:

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/cytoolz/functoolz.pyx in cytoolz.functoolz.curry.__call__()

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/formatting.py in apply_formatters(method, params, make_request, request_formatters, result_formatters, error_formatters)
     48         response = make_request(method, formatted_params)
     49     else:
---> 50         response = make_request(method, params)
     51
     52     if 'result' in response and method in result_formatters:

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/normalize_errors.py in middleware(method, params)
      7 def normalize_errors_middleware(make_request, web3):
      8     def middleware(method, params):
----> 9         result = make_request(method, params)
     10
     11         # As of v1.8, Geth returns errors when you request a

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/cytoolz/functoolz.pyx in cytoolz.functoolz.curry.__call__()

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/formatting.py in apply_formatters(method, params, make_request, request_formatters, result_formatters, error_formatters)
     48         response = make_request(method, formatted_params)
     49     else:
---> 50         response = make_request(method, params)
     51
     52     if 'result' in response and method in result_formatters:

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/cytoolz/functoolz.pyx in cytoolz.functoolz.curry.__call__()

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/formatting.py in apply_formatters(method, params, make_request, request_formatters, result_formatters, error_formatters)
     48         response = make_request(method, formatted_params)
     49     else:
---> 50         response = make_request(method, params)
     51
     52     if 'result' in response and method in result_formatters:

~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/providers/websocket.py in make_request(self, method, params)
    102             WebsocketProvider._loop
    103         )
--> 104         return future.result()

/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/concurrent/futures/_base.py in result(self, timeout)
    425                 return self.__get_result()
    426
--> 427             self._condition.wait(timeout)
    428
    429             if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:

/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py in wait(self, timeout)
    293         try:    # restore state no matter what (e.g., KeyboardInterrupt)
    294             if timeout is None:
--> 295                 waiter.acquire()
    296                 gotit = True
    297             else:

KeyboardInterrupt:
```

I have tried both `wss://ropsten.infura.io/ws` and `wss://ropsten.infura.io/_ws`. I have also posted the same question on Infura forum (https://github.com/INFURA/infura/issues/110). Trying to get help on both sides.

### How can it be fixed?

TBD.


--- 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/providers/websocket.py`
Content:
```
1 import asyncio
2 import json
3 import logging
4 import os
5 from threading import (
6     Thread,
7 )
8 
9 import websockets
10 
11 from web3.exceptions import (
12     ValidationError,
13 )
14 from web3.providers.base import (
15     JSONBaseProvider,
16 )
17 
18 RESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'}
19 
20 
21 def _start_event_loop(loop):
22     asyncio.set_event_loop(loop)
23     loop.run_forever()
24     loop.close()
25 
26 
27 def _get_threaded_loop():
28     new_loop = asyncio.new_event_loop()
29     thread_loop = Thread(target=_start_event_loop, args=(new_loop,), daemon=True)
30     thread_loop.start()
31     return new_loop
32 
33 
34 def get_default_endpoint():
35     return os.environ.get('WEB3_WS_PROVIDER_URI', 'ws://127.0.0.1:8546')
36 
37 
38 class PersistentWebSocket:
39 
40     def __init__(self, endpoint_uri, loop, websocket_kwargs):
41         self.ws = None
42         self.endpoint_uri = endpoint_uri
43         self.loop = loop
44         self.websocket_kwargs = websocket_kwargs
45 
46     async def __aenter__(self):
47         if self.ws is None:
48             self.ws = await websockets.connect(
49                 uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs
50             )
51         return self.ws
52 
53     async def __aexit__(self, exc_type, exc_val, exc_tb):
54         if exc_val is not None:
55             try:
56                 await self.ws.close()
57             except Exception:
58                 pass
59             self.ws = None
60 
61 
62 class WebsocketProvider(JSONBaseProvider):
63     logger = logging.getLogger("web3.providers.WebsocketProvider")
64     _loop = None
65 
66     def __init__(self, endpoint_uri=None, websocket_kwargs=None):
67         self.endpoint_uri = endpoint_uri
68         if self.endpoint_uri is None:
69             self.endpoint_uri = get_default_endpoint()
70         if WebsocketProvider._loop is None:
71             WebsocketProvider._loop = _get_threaded_loop()
72         if websocket_kwargs is None:
73             websocket_kwargs = {}
74         else:
75             found_restricted_keys = set(websocket_kwargs.keys()).intersection(
76                 RESTRICTED_WEBSOCKET_KWARGS
77             )
78             if found_restricted_keys:
79                 raise ValidationError(
80                     '{0} are not allowed in websocket_kwargs, '
81                     'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys)
82                 )
83         self.conn = PersistentWebSocket(
84             self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs
85         )
86         super().__init__()
87 
88     def __str__(self):
89         return "WS connection {0}".format(self.endpoint_uri)
90 
91     async def coro_make_request(self, request_data):
92         async with self.conn as conn:
93             await conn.send(request_data)
94             return json.loads(await conn.recv())
95 
96     def make_request(self, method, params):
97         self.logger.debug("Making request WebSocket. URI: %s, "
98                           "Method: %s", self.endpoint_uri, method)
99         request_data = self.encode_rpc_request(method, params)
100         future = asyncio.run_coroutine_threadsafe(
101             self.coro_make_request(request_data),
102             WebsocketProvider._loop
103         )
104         return future.result()
105
```
--- 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/providers/websocket.py b/web3/providers/websocket.py
--- a/web3/providers/websocket.py
+++ b/web3/providers/websocket.py
@@ -16,6 +16,7 @@
 )
 
 RESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'}
+DEFAULT_WEBSOCKET_TIMEOUT = 10
 
 
 def _start_event_loop(loop):
@@ -63,8 +64,14 @@
     logger = logging.getLogger("web3.providers.WebsocketProvider")
     _loop = None
 
-    def __init__(self, endpoint_uri=None, websocket_kwargs=None):
+    def __init__(
+            self,
+            endpoint_uri=None,
+            websocket_kwargs=None,
+            websocket_timeout=DEFAULT_WEBSOCKET_TIMEOUT
+    ):
         self.endpoint_uri = endpoint_uri
+        self.websocket_timeout = websocket_timeout
         if self.endpoint_uri is None:
             self.endpoint_uri = get_default_endpoint()
         if WebsocketProvider._loop is None:
@@ -90,8 +97,16 @@
 
     async def coro_make_request(self, request_data):
         async with self.conn as conn:
-            await conn.send(request_data)
-            return json.loads(await conn.recv())
+            await asyncio.wait_for(
+                conn.send(request_data),
+                timeout=self.websocket_timeout
+            )
+            return json.loads(
+                await asyncio.wait_for(
+                    conn.recv(),
+                    timeout=self.websocket_timeout
+                )
+            )
 
     def make_request(self, method, params):
         self.logger.debug("Making request WebSocket. URI: %s, "

{"golden_diff": "diff --git a/web3/providers/websocket.py b/web3/providers/websocket.py\n--- a/web3/providers/websocket.py\n+++ b/web3/providers/websocket.py\n@@ -16,6 +16,7 @@\n )\n \n RESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'}\n+DEFAULT_WEBSOCKET_TIMEOUT = 10\n \n \n def _start_event_loop(loop):\n@@ -63,8 +64,14 @@\n     logger = logging.getLogger(\"web3.providers.WebsocketProvider\")\n     _loop = None\n \n-    def __init__(self, endpoint_uri=None, websocket_kwargs=None):\n+    def __init__(\n+            self,\n+            endpoint_uri=None,\n+            websocket_kwargs=None,\n+            websocket_timeout=DEFAULT_WEBSOCKET_TIMEOUT\n+    ):\n         self.endpoint_uri = endpoint_uri\n+        self.websocket_timeout = websocket_timeout\n         if self.endpoint_uri is None:\n             self.endpoint_uri = get_default_endpoint()\n         if WebsocketProvider._loop is None:\n@@ -90,8 +97,16 @@\n \n     async def coro_make_request(self, request_data):\n         async with self.conn as conn:\n-            await conn.send(request_data)\n-            return json.loads(await conn.recv())\n+            await asyncio.wait_for(\n+                conn.send(request_data),\n+                timeout=self.websocket_timeout\n+            )\n+            return json.loads(\n+                await asyncio.wait_for(\n+                    conn.recv(),\n+                    timeout=self.websocket_timeout\n+                )\n+            )\n \n     def make_request(self, method, params):\n         self.logger.debug(\"Making request WebSocket. URI: %s, \"\n", "issue": "Connection to ropsten websocket over infura hang.\n* Version: 4.4.1\r\n* Python: 3.6\r\n* OS: osx\r\n\r\n\r\n### What was wrong?\r\n\r\nI wasn't able to connect to infura's ropsten websocket. The process hung when I wanted to create the filter object on pending transactions. It worked for both rinkeby's and mainnet's but just not for ropsten network.\r\n\r\nI was wondering if anyone is having the same issue. Given that I was able to connect to two other networks it makes it less suspicious that the problem is caused by the web3.py\r\n\r\nBelow is how I instantiate the filter by following https://web3py.readthedocs.io/en/stable/filters.html#web3.utils.filters.TransactionFilter\r\n```python\r\nIn [23]: web3 = Web3(Web3.WebsocketProvider('wss://rinkeby.infura.io/_ws'))\r\n\r\nIn [24]: new_transaction_filter = web3.eth.filter('pending')\r\n\r\nIn [25]: new_transaction_filter.get_new_entries()\r\nOut[25]:\r\n[HexBytes('0x4945f39cbeedd911eae93577c75d763d364c37ae01dcb7fb31ff9421a2a92b71'),\r\n HexBytes('0xae0b88b85163d3f0a918236cbf3ec93ccb589c6ad327e937f69b9591680fbf61'),\r\n```\r\n\r\nProcess hung on line 27 for a long time. I interrupted. The stacktrace is below.\r\n```python\r\nIn [26]: web3 = Web3(Web3.WebsocketProvider('wss://ropsten.infura.io/ws'))\r\n\r\nIn [27]: new_transaction_filter = web3.eth.filter('pending')\r\n\r\n^C---------------------------------------------------------------------------\r\nKeyboardInterrupt                         Traceback (most recent call last)\r\n<ipython-input-3-60afc0d44840> in <module>()\r\n----> 1 new_transaction_filter = web3.eth.filter('pending')\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/eth.py in filter(self, filter_params, filter_id)\r\n    319             elif filter_params == \"pending\":\r\n    320                 filter_id = self.web3.manager.request_blocking(\r\n--> 321                     \"eth_newPendingTransactionFilter\", [],\r\n    322                 )\r\n    323                 return TransactionFilter(self.web3, filter_id)\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/manager.py in request_blocking(self, method, params)\r\n    105         Make a synchronous request using the provider\r\n    106         \"\"\"\r\n--> 107         response = self._make_request(method, params)\r\n    108\r\n    109         if \"error\" in response:\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/manager.py in _make_request(self, method, params)\r\n     88             self.logger.debug(\"Making request. Method: %s\", method)\r\n     89             try:\r\n---> 90                 return request_func(method, params)\r\n     91             except CannotHandleRequest:\r\n     92                 continue\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/gas_price_strategy.py in middleware(method, params)\r\n     16                     transaction = assoc(transaction, 'gasPrice', generated_gas_price)\r\n     17                     return make_request(method, [transaction])\r\n---> 18         return make_request(method, params)\r\n     19     return middleware\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/cytoolz/functoolz.pyx in cytoolz.functoolz.curry.__call__()\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/formatting.py in apply_formatters(method, params, make_request, request_formatters, result_formatters, error_formatters)\r\n     48         response = make_request(method, formatted_params)\r\n     49     else:\r\n---> 50         response = make_request(method, params)\r\n     51\r\n     52     if 'result' in response and method in result_formatters:\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/attrdict.py in middleware(method, params)\r\n     16     \"\"\"\r\n     17     def middleware(method, params):\r\n---> 18         response = make_request(method, params)\r\n     19\r\n     20         if 'result' in response:\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/cytoolz/functoolz.pyx in cytoolz.functoolz.curry.__call__()\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/formatting.py in apply_formatters(method, params, make_request, request_formatters, result_formatters, error_formatters)\r\n     48         response = make_request(method, formatted_params)\r\n     49     else:\r\n---> 50         response = make_request(method, params)\r\n     51\r\n     52     if 'result' in response and method in result_formatters:\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/normalize_errors.py in middleware(method, params)\r\n      7 def normalize_errors_middleware(make_request, web3):\r\n      8     def middleware(method, params):\r\n----> 9         result = make_request(method, params)\r\n     10\r\n     11         # As of v1.8, Geth returns errors when you request a\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/cytoolz/functoolz.pyx in cytoolz.functoolz.curry.__call__()\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/formatting.py in apply_formatters(method, params, make_request, request_formatters, result_formatters, error_formatters)\r\n     48         response = make_request(method, formatted_params)\r\n     49     else:\r\n---> 50         response = make_request(method, params)\r\n     51\r\n     52     if 'result' in response and method in result_formatters:\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/cytoolz/functoolz.pyx in cytoolz.functoolz.curry.__call__()\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/middleware/formatting.py in apply_formatters(method, params, make_request, request_formatters, result_formatters, error_formatters)\r\n     48         response = make_request(method, formatted_params)\r\n     49     else:\r\n---> 50         response = make_request(method, params)\r\n     51\r\n     52     if 'result' in response and method in result_formatters:\r\n\r\n~/pg/some_name/virtualenv_run/lib/python3.6/site-packages/web3/providers/websocket.py in make_request(self, method, params)\r\n    102             WebsocketProvider._loop\r\n    103         )\r\n--> 104         return future.result()\r\n\r\n/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/concurrent/futures/_base.py in result(self, timeout)\r\n    425                 return self.__get_result()\r\n    426\r\n--> 427             self._condition.wait(timeout)\r\n    428\r\n    429             if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:\r\n\r\n/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py in wait(self, timeout)\r\n    293         try:    # restore state no matter what (e.g., KeyboardInterrupt)\r\n    294             if timeout is None:\r\n--> 295                 waiter.acquire()\r\n    296                 gotit = True\r\n    297             else:\r\n\r\nKeyboardInterrupt:\r\n```\r\n\r\nI have tried both `wss://ropsten.infura.io/ws` and `wss://ropsten.infura.io/_ws`. I have also posted the same question on Infura forum (https://github.com/INFURA/infura/issues/110). Trying to get help on both sides.\r\n\r\n### How can it be fixed?\r\n\r\nTBD.\r\n\n", "before_files": [{"content": "import asyncio\nimport json\nimport logging\nimport os\nfrom threading import (\n    Thread,\n)\n\nimport websockets\n\nfrom web3.exceptions import (\n    ValidationError,\n)\nfrom web3.providers.base import (\n    JSONBaseProvider,\n)\n\nRESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'}\n\n\ndef _start_event_loop(loop):\n    asyncio.set_event_loop(loop)\n    loop.run_forever()\n    loop.close()\n\n\ndef _get_threaded_loop():\n    new_loop = asyncio.new_event_loop()\n    thread_loop = Thread(target=_start_event_loop, args=(new_loop,), daemon=True)\n    thread_loop.start()\n    return new_loop\n\n\ndef get_default_endpoint():\n    return os.environ.get('WEB3_WS_PROVIDER_URI', 'ws://127.0.0.1:8546')\n\n\nclass PersistentWebSocket:\n\n    def __init__(self, endpoint_uri, loop, websocket_kwargs):\n        self.ws = None\n        self.endpoint_uri = endpoint_uri\n        self.loop = loop\n        self.websocket_kwargs = websocket_kwargs\n\n    async def __aenter__(self):\n        if self.ws is None:\n            self.ws = await websockets.connect(\n                uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs\n            )\n        return self.ws\n\n    async def __aexit__(self, exc_type, exc_val, exc_tb):\n        if exc_val is not None:\n            try:\n                await self.ws.close()\n            except Exception:\n                pass\n            self.ws = None\n\n\nclass WebsocketProvider(JSONBaseProvider):\n    logger = logging.getLogger(\"web3.providers.WebsocketProvider\")\n    _loop = None\n\n    def __init__(self, endpoint_uri=None, websocket_kwargs=None):\n        self.endpoint_uri = endpoint_uri\n        if self.endpoint_uri is None:\n            self.endpoint_uri = get_default_endpoint()\n        if WebsocketProvider._loop is None:\n            WebsocketProvider._loop = _get_threaded_loop()\n        if websocket_kwargs is None:\n            websocket_kwargs = {}\n        else:\n            found_restricted_keys = set(websocket_kwargs.keys()).intersection(\n                RESTRICTED_WEBSOCKET_KWARGS\n            )\n            if found_restricted_keys:\n                raise ValidationError(\n                    '{0} are not allowed in websocket_kwargs, '\n                    'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys)\n                )\n        self.conn = PersistentWebSocket(\n            self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs\n        )\n        super().__init__()\n\n    def __str__(self):\n        return \"WS connection {0}\".format(self.endpoint_uri)\n\n    async def coro_make_request(self, request_data):\n        async with self.conn as conn:\n            await conn.send(request_data)\n            return json.loads(await conn.recv())\n\n    def make_request(self, method, params):\n        self.logger.debug(\"Making request WebSocket. URI: %s, \"\n                          \"Method: %s\", self.endpoint_uri, method)\n        request_data = self.encode_rpc_request(method, params)\n        future = asyncio.run_coroutine_threadsafe(\n            self.coro_make_request(request_data),\n            WebsocketProvider._loop\n        )\n        return future.result()\n", "path": "web3/providers/websocket.py"}], "after_files": [{"content": "import asyncio\nimport json\nimport logging\nimport os\nfrom threading import (\n    Thread,\n)\n\nimport websockets\n\nfrom web3.exceptions import (\n    ValidationError,\n)\nfrom web3.providers.base import (\n    JSONBaseProvider,\n)\n\nRESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'}\nDEFAULT_WEBSOCKET_TIMEOUT = 10\n\n\ndef _start_event_loop(loop):\n    asyncio.set_event_loop(loop)\n    loop.run_forever()\n    loop.close()\n\n\ndef _get_threaded_loop():\n    new_loop = asyncio.new_event_loop()\n    thread_loop = Thread(target=_start_event_loop, args=(new_loop,), daemon=True)\n    thread_loop.start()\n    return new_loop\n\n\ndef get_default_endpoint():\n    return os.environ.get('WEB3_WS_PROVIDER_URI', 'ws://127.0.0.1:8546')\n\n\nclass PersistentWebSocket:\n\n    def __init__(self, endpoint_uri, loop, websocket_kwargs):\n        self.ws = None\n        self.endpoint_uri = endpoint_uri\n        self.loop = loop\n        self.websocket_kwargs = websocket_kwargs\n\n    async def __aenter__(self):\n        if self.ws is None:\n            self.ws = await websockets.connect(\n                uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs\n            )\n        return self.ws\n\n    async def __aexit__(self, exc_type, exc_val, exc_tb):\n        if exc_val is not None:\n            try:\n                await self.ws.close()\n            except Exception:\n                pass\n            self.ws = None\n\n\nclass WebsocketProvider(JSONBaseProvider):\n    logger = logging.getLogger(\"web3.providers.WebsocketProvider\")\n    _loop = None\n\n    def __init__(\n            self,\n            endpoint_uri=None,\n            websocket_kwargs=None,\n            websocket_timeout=DEFAULT_WEBSOCKET_TIMEOUT\n    ):\n        self.endpoint_uri = endpoint_uri\n        self.websocket_timeout = websocket_timeout\n        if self.endpoint_uri is None:\n            self.endpoint_uri = get_default_endpoint()\n        if WebsocketProvider._loop is None:\n            WebsocketProvider._loop = _get_threaded_loop()\n        if websocket_kwargs is None:\n            websocket_kwargs = {}\n        else:\n            found_restricted_keys = set(websocket_kwargs.keys()).intersection(\n                RESTRICTED_WEBSOCKET_KWARGS\n            )\n            if found_restricted_keys:\n                raise ValidationError(\n                    '{0} are not allowed in websocket_kwargs, '\n                    'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys)\n                )\n        self.conn = PersistentWebSocket(\n            self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs\n        )\n        super().__init__()\n\n    def __str__(self):\n        return \"WS connection {0}\".format(self.endpoint_uri)\n\n    async def coro_make_request(self, request_data):\n        async with self.conn as conn:\n            await asyncio.wait_for(\n                conn.send(request_data),\n                timeout=self.websocket_timeout\n            )\n            return json.loads(\n                await asyncio.wait_for(\n                    conn.recv(),\n                    timeout=self.websocket_timeout\n                )\n            )\n\n    def make_request(self, method, params):\n        self.logger.debug(\"Making request WebSocket. URI: %s, \"\n                          \"Method: %s\", self.endpoint_uri, method)\n        request_data = self.encode_rpc_request(method, params)\n        future = asyncio.run_coroutine_threadsafe(\n            self.coro_make_request(request_data),\n            WebsocketProvider._loop\n        )\n        return future.result()\n", "path": "web3/providers/websocket.py"}]}