We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Upgrade websockets to 10.1 ?
* Version: 5.25.0
* Python: 3.8
* OS: osx
* `pip freeze` output

```
solana==0.19.0 (problem)
```


### What was wrong?

Currently in dependency hell trying to use solana-py and web3 in same service. solana-py requires websockets 10.1, web3 seems to not support that. Below is the changelog for 10.1 release of websockets, there are no incompatible changes. Any chance I could bother the team to consider upgrading support to 10.1?

Also, from my logs it seems web3 needs "websockets<10" but i think changes were made to support 10.0 (#2175) . May need to update requirements:

remote:        The conflict is caused by:
remote:            solana 0.19.0 depends on websockets<11.0 and >=10.1
remote:            web3 5.25.0 depends on websockets<10 and >=9.1

https://websockets.readthedocs.io/en/latest/project/changelog.html#id3

Thanks!


### How can it be fixed?

Upgrade websockets support to 10.1

---
**Note:** We prefer to use issues to track our work. If you think you've encountered a bug in web3py or 
have a feature request, you're in the right place. If you have implementation or usage questions, 
please refer to our [documentation](https://web3py.readthedocs.io/en/latest/) and/or join the conversation 
on [discord](https://discord.gg/GHryRvPB84).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `setup.py`
Content:
```
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 from setuptools import (
4     find_packages,
5     setup,
6 )
7 
8 extras_require = {
9     'tester': [
10         "eth-tester[py-evm]==v0.6.0-beta.6",
11         "py-geth>=3.7.0,<4",
12     ],
13     'linter': [
14         "flake8==3.8.3",
15         "isort>=4.2.15,<4.3.5",
16         "mypy==0.910",
17         "types-setuptools>=57.4.4,<58",
18         "types-requests>=2.26.1,<3",
19         "types-protobuf>=3.18.2,<4",
20     ],
21     'docs': [
22         "mock",
23         "sphinx-better-theme>=0.1.4",
24         "click>=5.1",
25         "configparser==3.5.0",
26         "contextlib2>=0.5.4",
27         "py-geth>=3.6.0,<4",
28         "py-solc>=0.4.0",
29         "pytest>=4.4.0,<5.0.0",
30         "sphinx>=3.0,<4",
31         "sphinx_rtd_theme>=0.1.9",
32         "toposort>=1.4",
33         "towncrier==18.5.0",
34         "urllib3",
35         "wheel"
36     ],
37     'dev': [
38         "bumpversion",
39         "flaky>=3.7.0,<4",
40         "hypothesis>=3.31.2,<6",
41         "pytest>=4.4.0,<5.0.0",
42         "pytest-asyncio>=0.10.0,<0.11",
43         "pytest-mock>=1.10,<2",
44         "pytest-pythonpath>=0.3",
45         "pytest-watch>=4.2,<5",
46         "pytest-xdist>=1.29,<2",
47         "setuptools>=38.6.0",
48         "tox>=1.8.0",
49         "tqdm>4.32,<5",
50         "twine>=1.13,<2",
51         "pluggy==0.13.1",
52         "when-changed>=0.3.0,<0.4"
53     ]
54 }
55 
56 extras_require['dev'] = (
57     extras_require['tester']
58     + extras_require['linter']
59     + extras_require['docs']
60     + extras_require['dev']
61 )
62 
63 with open('./README.md') as readme:
64     long_description = readme.read()
65 
66 setup(
67     name='web3',
68     # *IMPORTANT*: Don't manually change the version here. Use the 'bumpversion' utility.
69     version='5.28.0',
70     description="""Web3.py""",
71     long_description_content_type='text/markdown',
72     long_description=long_description,
73     author='Piper Merriam',
74     author_email='pipermerriam@gmail.com',
75     url='https://github.com/ethereum/web3.py',
76     include_package_data=True,
77     install_requires=[
78         "aiohttp>=3.7.4.post0,<4",
79         "eth-abi>=2.0.0b6,<3.0.0",
80         "eth-account>=0.5.7,<0.6.0",
81         "eth-hash[pycryptodome]>=0.2.0,<1.0.0",
82         "eth-typing>=2.0.0,<3.0.0",
83         "eth-utils>=1.9.5,<2.0.0",
84         "hexbytes>=0.1.0,<1.0.0",
85         "ipfshttpclient==0.8.0a2",
86         "jsonschema>=3.2.0,<4.0.0",
87         "lru-dict>=1.1.6,<2.0.0",
88         "protobuf>=3.10.0,<4",
89         "pywin32>=223;platform_system=='Windows'",
90         "requests>=2.16.0,<3.0.0",
91         # remove typing_extensions after python_requires>=3.8, see web3._utils.compat
92         "typing-extensions>=3.7.4.1,<5;python_version<'3.8'",
93         "websockets>=9.1,<10",
94     ],
95     python_requires='>=3.7,<3.10',
96     extras_require=extras_require,
97     py_modules=['web3', 'ens', 'ethpm'],
98     entry_points={"pytest11": ["pytest_ethereum = web3.tools.pytest_ethereum.plugins"]},
99     license="MIT",
100     zip_safe=False,
101     keywords='ethereum',
102     packages=find_packages(exclude=["tests", "tests.*"]),
103     package_data={"web3": ["py.typed"]},
104     classifiers=[
105         'Development Status :: 5 - Production/Stable',
106         'Intended Audience :: Developers',
107         'License :: OSI Approved :: MIT License',
108         'Natural Language :: English',
109         'Programming Language :: Python :: 3',
110         'Programming Language :: Python :: 3.7',
111         'Programming Language :: Python :: 3.8',
112         'Programming Language :: Python :: 3.9',
113     ],
114 )
115
```

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 from types import (
9     TracebackType,
10 )
11 from typing import (
12     Any,
13     Optional,
14     Type,
15     Union,
16 )
17 
18 from eth_typing import (
19     URI,
20 )
21 from websockets.client import (
22     connect,
23 )
24 from websockets.legacy.client import (
25     WebSocketClientProtocol,
26 )
27 
28 from web3.exceptions import (
29     ValidationError,
30 )
31 from web3.providers.base import (
32     JSONBaseProvider,
33 )
34 from web3.types import (
35     RPCEndpoint,
36     RPCResponse,
37 )
38 
39 RESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'}
40 DEFAULT_WEBSOCKET_TIMEOUT = 10
41 
42 
43 def _start_event_loop(loop: asyncio.AbstractEventLoop) -> None:
44     asyncio.set_event_loop(loop)
45     loop.run_forever()
46     loop.close()
47 
48 
49 def _get_threaded_loop() -> asyncio.AbstractEventLoop:
50     new_loop = asyncio.new_event_loop()
51     thread_loop = Thread(target=_start_event_loop, args=(new_loop,), daemon=True)
52     thread_loop.start()
53     return new_loop
54 
55 
56 def get_default_endpoint() -> URI:
57     return URI(os.environ.get('WEB3_WS_PROVIDER_URI', 'ws://127.0.0.1:8546'))
58 
59 
60 class PersistentWebSocket:
61 
62     def __init__(
63         self, endpoint_uri: URI, loop: asyncio.AbstractEventLoop, websocket_kwargs: Any
64     ) -> None:
65         self.ws: WebSocketClientProtocol = None
66         self.endpoint_uri = endpoint_uri
67         self.loop = loop
68         self.websocket_kwargs = websocket_kwargs
69 
70     async def __aenter__(self) -> WebSocketClientProtocol:
71         if self.ws is None:
72             self.ws = await connect(
73                 uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs
74             )
75         return self.ws
76 
77     async def __aexit__(
78         self, exc_type: Type[BaseException], exc_val: BaseException, exc_tb: TracebackType
79     ) -> None:
80         if exc_val is not None:
81             try:
82                 await self.ws.close()
83             except Exception:
84                 pass
85             self.ws = None
86 
87 
88 class WebsocketProvider(JSONBaseProvider):
89     logger = logging.getLogger("web3.providers.WebsocketProvider")
90     _loop = None
91 
92     def __init__(
93         self,
94         endpoint_uri: Optional[Union[URI, str]] = None,
95         websocket_kwargs: Optional[Any] = None,
96         websocket_timeout: int = DEFAULT_WEBSOCKET_TIMEOUT,
97     ) -> None:
98         self.endpoint_uri = URI(endpoint_uri)
99         self.websocket_timeout = websocket_timeout
100         if self.endpoint_uri is None:
101             self.endpoint_uri = get_default_endpoint()
102         if WebsocketProvider._loop is None:
103             WebsocketProvider._loop = _get_threaded_loop()
104         if websocket_kwargs is None:
105             websocket_kwargs = {}
106         else:
107             found_restricted_keys = set(websocket_kwargs.keys()).intersection(
108                 RESTRICTED_WEBSOCKET_KWARGS
109             )
110             if found_restricted_keys:
111                 raise ValidationError(
112                     '{0} are not allowed in websocket_kwargs, '
113                     'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys)
114                 )
115         self.conn = PersistentWebSocket(
116             self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs
117         )
118         super().__init__()
119 
120     def __str__(self) -> str:
121         return "WS connection {0}".format(self.endpoint_uri)
122 
123     async def coro_make_request(self, request_data: bytes) -> RPCResponse:
124         async with self.conn as conn:
125             await asyncio.wait_for(
126                 conn.send(request_data),
127                 timeout=self.websocket_timeout
128             )
129             return json.loads(
130                 await asyncio.wait_for(
131                     conn.recv(),
132                     timeout=self.websocket_timeout
133                 )
134             )
135 
136     def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
137         self.logger.debug("Making request WebSocket. URI: %s, "
138                           "Method: %s", self.endpoint_uri, method)
139         request_data = self.encode_rpc_request(method, params)
140         future = asyncio.run_coroutine_threadsafe(
141             self.coro_make_request(request_data),
142             WebsocketProvider._loop
143         )
144         return future.result()
145
```
--- 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/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -90,7 +90,7 @@
         "requests>=2.16.0,<3.0.0",
         # remove typing_extensions after python_requires>=3.8, see web3._utils.compat
         "typing-extensions>=3.7.4.1,<5;python_version<'3.8'",
-        "websockets>=9.1,<10",
+        "websockets>=10.0.0,<11",
     ],
     python_requires='>=3.7,<3.10',
     extras_require=extras_require,
diff --git a/web3/providers/websocket.py b/web3/providers/websocket.py
--- a/web3/providers/websocket.py
+++ b/web3/providers/websocket.py
@@ -60,17 +60,16 @@
 class PersistentWebSocket:
 
     def __init__(
-        self, endpoint_uri: URI, loop: asyncio.AbstractEventLoop, websocket_kwargs: Any
+        self, endpoint_uri: URI, websocket_kwargs: Any
     ) -> None:
         self.ws: WebSocketClientProtocol = None
         self.endpoint_uri = endpoint_uri
-        self.loop = loop
         self.websocket_kwargs = websocket_kwargs
 
     async def __aenter__(self) -> WebSocketClientProtocol:
         if self.ws is None:
             self.ws = await connect(
-                uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs
+                uri=self.endpoint_uri, **self.websocket_kwargs
             )
         return self.ws
 
@@ -113,7 +112,7 @@
                     'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys)
                 )
         self.conn = PersistentWebSocket(
-            self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs
+            self.endpoint_uri, websocket_kwargs
         )
         super().__init__()

{"golden_diff": "diff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -90,7 +90,7 @@\n         \"requests>=2.16.0,<3.0.0\",\n         # remove typing_extensions after python_requires>=3.8, see web3._utils.compat\n         \"typing-extensions>=3.7.4.1,<5;python_version<'3.8'\",\n-        \"websockets>=9.1,<10\",\n+        \"websockets>=10.0.0,<11\",\n     ],\n     python_requires='>=3.7,<3.10',\n     extras_require=extras_require,\ndiff --git a/web3/providers/websocket.py b/web3/providers/websocket.py\n--- a/web3/providers/websocket.py\n+++ b/web3/providers/websocket.py\n@@ -60,17 +60,16 @@\n class PersistentWebSocket:\n \n     def __init__(\n-        self, endpoint_uri: URI, loop: asyncio.AbstractEventLoop, websocket_kwargs: Any\n+        self, endpoint_uri: URI, websocket_kwargs: Any\n     ) -> None:\n         self.ws: WebSocketClientProtocol = None\n         self.endpoint_uri = endpoint_uri\n-        self.loop = loop\n         self.websocket_kwargs = websocket_kwargs\n \n     async def __aenter__(self) -> WebSocketClientProtocol:\n         if self.ws is None:\n             self.ws = await connect(\n-                uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs\n+                uri=self.endpoint_uri, **self.websocket_kwargs\n             )\n         return self.ws\n \n@@ -113,7 +112,7 @@\n                     'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys)\n                 )\n         self.conn = PersistentWebSocket(\n-            self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs\n+            self.endpoint_uri, websocket_kwargs\n         )\n         super().__init__()\n", "issue": "Upgrade websockets to 10.1 ?\n* Version: 5.25.0\r\n* Python: 3.8\r\n* OS: osx\r\n* `pip freeze` output\r\n\r\n```\r\nsolana==0.19.0 (problem)\r\n```\r\n\r\n\r\n### What was wrong?\r\n\r\nCurrently in dependency hell trying to use solana-py and web3 in same service. solana-py requires websockets 10.1, web3 seems to not support that. Below is the changelog for 10.1 release of websockets, there are no incompatible changes. Any chance I could bother the team to consider upgrading support to 10.1?\r\n\r\nAlso, from my logs it seems web3 needs \"websockets<10\" but i think changes were made to support 10.0 (#2175) . May need to update requirements:\r\n\r\nremote:        The conflict is caused by:\r\nremote:            solana 0.19.0 depends on websockets<11.0 and >=10.1\r\nremote:            web3 5.25.0 depends on websockets<10 and >=9.1\r\n\r\nhttps://websockets.readthedocs.io/en/latest/project/changelog.html#id3\r\n\r\nThanks!\r\n\r\n\r\n### How can it be fixed?\r\n\r\nUpgrade websockets support to 10.1\r\n\r\n---\r\n**Note:** We prefer to use issues to track our work. If you think you've encountered a bug in web3py or \r\nhave a feature request, you're in the right place. If you have implementation or usage questions, \r\nplease refer to our [documentation](https://web3py.readthedocs.io/en/latest/) and/or join the conversation \r\non [discord](https://discord.gg/GHryRvPB84).\r\n\n", "before_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom setuptools import (\n    find_packages,\n    setup,\n)\n\nextras_require = {\n    'tester': [\n        \"eth-tester[py-evm]==v0.6.0-beta.6\",\n        \"py-geth>=3.7.0,<4\",\n    ],\n    'linter': [\n        \"flake8==3.8.3\",\n        \"isort>=4.2.15,<4.3.5\",\n        \"mypy==0.910\",\n        \"types-setuptools>=57.4.4,<58\",\n        \"types-requests>=2.26.1,<3\",\n        \"types-protobuf>=3.18.2,<4\",\n    ],\n    'docs': [\n        \"mock\",\n        \"sphinx-better-theme>=0.1.4\",\n        \"click>=5.1\",\n        \"configparser==3.5.0\",\n        \"contextlib2>=0.5.4\",\n        \"py-geth>=3.6.0,<4\",\n        \"py-solc>=0.4.0\",\n        \"pytest>=4.4.0,<5.0.0\",\n        \"sphinx>=3.0,<4\",\n        \"sphinx_rtd_theme>=0.1.9\",\n        \"toposort>=1.4\",\n        \"towncrier==18.5.0\",\n        \"urllib3\",\n        \"wheel\"\n    ],\n    'dev': [\n        \"bumpversion\",\n        \"flaky>=3.7.0,<4\",\n        \"hypothesis>=3.31.2,<6\",\n        \"pytest>=4.4.0,<5.0.0\",\n        \"pytest-asyncio>=0.10.0,<0.11\",\n        \"pytest-mock>=1.10,<2\",\n        \"pytest-pythonpath>=0.3\",\n        \"pytest-watch>=4.2,<5\",\n        \"pytest-xdist>=1.29,<2\",\n        \"setuptools>=38.6.0\",\n        \"tox>=1.8.0\",\n        \"tqdm>4.32,<5\",\n        \"twine>=1.13,<2\",\n        \"pluggy==0.13.1\",\n        \"when-changed>=0.3.0,<0.4\"\n    ]\n}\n\nextras_require['dev'] = (\n    extras_require['tester']\n    + extras_require['linter']\n    + extras_require['docs']\n    + extras_require['dev']\n)\n\nwith open('./README.md') as readme:\n    long_description = readme.read()\n\nsetup(\n    name='web3',\n    # *IMPORTANT*: Don't manually change the version here. Use the 'bumpversion' utility.\n    version='5.28.0',\n    description=\"\"\"Web3.py\"\"\",\n    long_description_content_type='text/markdown',\n    long_description=long_description,\n    author='Piper Merriam',\n    author_email='pipermerriam@gmail.com',\n    url='https://github.com/ethereum/web3.py',\n    include_package_data=True,\n    install_requires=[\n        \"aiohttp>=3.7.4.post0,<4\",\n        \"eth-abi>=2.0.0b6,<3.0.0\",\n        \"eth-account>=0.5.7,<0.6.0\",\n        \"eth-hash[pycryptodome]>=0.2.0,<1.0.0\",\n        \"eth-typing>=2.0.0,<3.0.0\",\n        \"eth-utils>=1.9.5,<2.0.0\",\n        \"hexbytes>=0.1.0,<1.0.0\",\n        \"ipfshttpclient==0.8.0a2\",\n        \"jsonschema>=3.2.0,<4.0.0\",\n        \"lru-dict>=1.1.6,<2.0.0\",\n        \"protobuf>=3.10.0,<4\",\n        \"pywin32>=223;platform_system=='Windows'\",\n        \"requests>=2.16.0,<3.0.0\",\n        # remove typing_extensions after python_requires>=3.8, see web3._utils.compat\n        \"typing-extensions>=3.7.4.1,<5;python_version<'3.8'\",\n        \"websockets>=9.1,<10\",\n    ],\n    python_requires='>=3.7,<3.10',\n    extras_require=extras_require,\n    py_modules=['web3', 'ens', 'ethpm'],\n    entry_points={\"pytest11\": [\"pytest_ethereum = web3.tools.pytest_ethereum.plugins\"]},\n    license=\"MIT\",\n    zip_safe=False,\n    keywords='ethereum',\n    packages=find_packages(exclude=[\"tests\", \"tests.*\"]),\n    package_data={\"web3\": [\"py.typed\"]},\n    classifiers=[\n        'Development Status :: 5 - Production/Stable',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: MIT License',\n        'Natural Language :: English',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.7',\n        'Programming Language :: Python :: 3.8',\n        'Programming Language :: Python :: 3.9',\n    ],\n)\n", "path": "setup.py"}, {"content": "import asyncio\nimport json\nimport logging\nimport os\nfrom threading import (\n    Thread,\n)\nfrom types import (\n    TracebackType,\n)\nfrom typing import (\n    Any,\n    Optional,\n    Type,\n    Union,\n)\n\nfrom eth_typing import (\n    URI,\n)\nfrom websockets.client import (\n    connect,\n)\nfrom websockets.legacy.client import (\n    WebSocketClientProtocol,\n)\n\nfrom web3.exceptions import (\n    ValidationError,\n)\nfrom web3.providers.base import (\n    JSONBaseProvider,\n)\nfrom web3.types import (\n    RPCEndpoint,\n    RPCResponse,\n)\n\nRESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'}\nDEFAULT_WEBSOCKET_TIMEOUT = 10\n\n\ndef _start_event_loop(loop: asyncio.AbstractEventLoop) -> None:\n    asyncio.set_event_loop(loop)\n    loop.run_forever()\n    loop.close()\n\n\ndef _get_threaded_loop() -> asyncio.AbstractEventLoop:\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() -> URI:\n    return URI(os.environ.get('WEB3_WS_PROVIDER_URI', 'ws://127.0.0.1:8546'))\n\n\nclass PersistentWebSocket:\n\n    def __init__(\n        self, endpoint_uri: URI, loop: asyncio.AbstractEventLoop, websocket_kwargs: Any\n    ) -> None:\n        self.ws: WebSocketClientProtocol = None\n        self.endpoint_uri = endpoint_uri\n        self.loop = loop\n        self.websocket_kwargs = websocket_kwargs\n\n    async def __aenter__(self) -> WebSocketClientProtocol:\n        if self.ws is None:\n            self.ws = await connect(\n                uri=self.endpoint_uri, loop=self.loop, **self.websocket_kwargs\n            )\n        return self.ws\n\n    async def __aexit__(\n        self, exc_type: Type[BaseException], exc_val: BaseException, exc_tb: TracebackType\n    ) -> None:\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: Optional[Union[URI, str]] = None,\n        websocket_kwargs: Optional[Any] = None,\n        websocket_timeout: int = DEFAULT_WEBSOCKET_TIMEOUT,\n    ) -> None:\n        self.endpoint_uri = 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) -> str:\n        return \"WS connection {0}\".format(self.endpoint_uri)\n\n    async def coro_make_request(self, request_data: bytes) -> RPCResponse:\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: RPCEndpoint, params: Any) -> RPCResponse:\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": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom setuptools import (\n    find_packages,\n    setup,\n)\n\nextras_require = {\n    'tester': [\n        \"eth-tester[py-evm]==v0.6.0-beta.6\",\n        \"py-geth>=3.7.0,<4\",\n    ],\n    'linter': [\n        \"flake8==3.8.3\",\n        \"isort>=4.2.15,<4.3.5\",\n        \"mypy==0.910\",\n        \"types-setuptools>=57.4.4,<58\",\n        \"types-requests>=2.26.1,<3\",\n        \"types-protobuf>=3.18.2,<4\",\n    ],\n    'docs': [\n        \"mock\",\n        \"sphinx-better-theme>=0.1.4\",\n        \"click>=5.1\",\n        \"configparser==3.5.0\",\n        \"contextlib2>=0.5.4\",\n        \"py-geth>=3.6.0,<4\",\n        \"py-solc>=0.4.0\",\n        \"pytest>=4.4.0,<5.0.0\",\n        \"sphinx>=3.0,<4\",\n        \"sphinx_rtd_theme>=0.1.9\",\n        \"toposort>=1.4\",\n        \"towncrier==18.5.0\",\n        \"urllib3\",\n        \"wheel\"\n    ],\n    'dev': [\n        \"bumpversion\",\n        \"flaky>=3.7.0,<4\",\n        \"hypothesis>=3.31.2,<6\",\n        \"pytest>=4.4.0,<5.0.0\",\n        \"pytest-asyncio>=0.10.0,<0.11\",\n        \"pytest-mock>=1.10,<2\",\n        \"pytest-pythonpath>=0.3\",\n        \"pytest-watch>=4.2,<5\",\n        \"pytest-xdist>=1.29,<2\",\n        \"setuptools>=38.6.0\",\n        \"tox>=1.8.0\",\n        \"tqdm>4.32,<5\",\n        \"twine>=1.13,<2\",\n        \"pluggy==0.13.1\",\n        \"when-changed>=0.3.0,<0.4\"\n    ]\n}\n\nextras_require['dev'] = (\n    extras_require['tester']\n    + extras_require['linter']\n    + extras_require['docs']\n    + extras_require['dev']\n)\n\nwith open('./README.md') as readme:\n    long_description = readme.read()\n\nsetup(\n    name='web3',\n    # *IMPORTANT*: Don't manually change the version here. Use the 'bumpversion' utility.\n    version='5.28.0',\n    description=\"\"\"Web3.py\"\"\",\n    long_description_content_type='text/markdown',\n    long_description=long_description,\n    author='Piper Merriam',\n    author_email='pipermerriam@gmail.com',\n    url='https://github.com/ethereum/web3.py',\n    include_package_data=True,\n    install_requires=[\n        \"aiohttp>=3.7.4.post0,<4\",\n        \"eth-abi>=2.0.0b6,<3.0.0\",\n        \"eth-account>=0.5.7,<0.6.0\",\n        \"eth-hash[pycryptodome]>=0.2.0,<1.0.0\",\n        \"eth-typing>=2.0.0,<3.0.0\",\n        \"eth-utils>=1.9.5,<2.0.0\",\n        \"hexbytes>=0.1.0,<1.0.0\",\n        \"ipfshttpclient==0.8.0a2\",\n        \"jsonschema>=3.2.0,<4.0.0\",\n        \"lru-dict>=1.1.6,<2.0.0\",\n        \"protobuf>=3.10.0,<4\",\n        \"pywin32>=223;platform_system=='Windows'\",\n        \"requests>=2.16.0,<3.0.0\",\n        # remove typing_extensions after python_requires>=3.8, see web3._utils.compat\n        \"typing-extensions>=3.7.4.1,<5;python_version<'3.8'\",\n        \"websockets>=10.0.0,<11\",\n    ],\n    python_requires='>=3.7,<3.10',\n    extras_require=extras_require,\n    py_modules=['web3', 'ens', 'ethpm'],\n    entry_points={\"pytest11\": [\"pytest_ethereum = web3.tools.pytest_ethereum.plugins\"]},\n    license=\"MIT\",\n    zip_safe=False,\n    keywords='ethereum',\n    packages=find_packages(exclude=[\"tests\", \"tests.*\"]),\n    package_data={\"web3\": [\"py.typed\"]},\n    classifiers=[\n        'Development Status :: 5 - Production/Stable',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: MIT License',\n        'Natural Language :: English',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.7',\n        'Programming Language :: Python :: 3.8',\n        'Programming Language :: Python :: 3.9',\n    ],\n)\n", "path": "setup.py"}, {"content": "import asyncio\nimport json\nimport logging\nimport os\nfrom threading import (\n    Thread,\n)\nfrom types import (\n    TracebackType,\n)\nfrom typing import (\n    Any,\n    Optional,\n    Type,\n    Union,\n)\n\nfrom eth_typing import (\n    URI,\n)\nfrom websockets.client import (\n    connect,\n)\nfrom websockets.legacy.client import (\n    WebSocketClientProtocol,\n)\n\nfrom web3.exceptions import (\n    ValidationError,\n)\nfrom web3.providers.base import (\n    JSONBaseProvider,\n)\nfrom web3.types import (\n    RPCEndpoint,\n    RPCResponse,\n)\n\nRESTRICTED_WEBSOCKET_KWARGS = {'uri', 'loop'}\nDEFAULT_WEBSOCKET_TIMEOUT = 10\n\n\ndef _start_event_loop(loop: asyncio.AbstractEventLoop) -> None:\n    asyncio.set_event_loop(loop)\n    loop.run_forever()\n    loop.close()\n\n\ndef _get_threaded_loop() -> asyncio.AbstractEventLoop:\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() -> URI:\n    return URI(os.environ.get('WEB3_WS_PROVIDER_URI', 'ws://127.0.0.1:8546'))\n\n\nclass PersistentWebSocket:\n\n    def __init__(\n        self, endpoint_uri: URI, websocket_kwargs: Any\n    ) -> None:\n        self.ws: WebSocketClientProtocol = None\n        self.endpoint_uri = endpoint_uri\n        self.websocket_kwargs = websocket_kwargs\n\n    async def __aenter__(self) -> WebSocketClientProtocol:\n        if self.ws is None:\n            self.ws = await connect(\n                uri=self.endpoint_uri, **self.websocket_kwargs\n            )\n        return self.ws\n\n    async def __aexit__(\n        self, exc_type: Type[BaseException], exc_val: BaseException, exc_tb: TracebackType\n    ) -> None:\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: Optional[Union[URI, str]] = None,\n        websocket_kwargs: Optional[Any] = None,\n        websocket_timeout: int = DEFAULT_WEBSOCKET_TIMEOUT,\n    ) -> None:\n        self.endpoint_uri = 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, websocket_kwargs\n        )\n        super().__init__()\n\n    def __str__(self) -> str:\n        return \"WS connection {0}\".format(self.endpoint_uri)\n\n    async def coro_make_request(self, request_data: bytes) -> RPCResponse:\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: RPCEndpoint, params: Any) -> RPCResponse:\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"}]}