We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Socket in use error when sending epp messages
### Current Behavior

Occasionally, we are unable to send messages via epp because of the following error:
"InfoDomain failed to execute due to an unknown error. Error: This socket is already used by another greenlet: <bound method Waiter.switch of <gevent._gevent_c_waiter.Waiter object at 0x7f8e1e5f6750>>"

### Expected Behavior

No error should occur.

### Steps to Reproduce

reproduction steps unknown, this happens irregularly and we haven't narrowed down a specif set of easily followed steps that can always guarantee this occurs. see additional context


### Environment

_No response_

### Additional Context

This bug started after we added gunicorn using gevent instead of just the default sync option. While we still should be asynchronious this creates an issue with how we are using our sockets on epp calls. We were able to see this more with the removal of the connection pool. 

### Issue Links

_No response_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/epplibwrapper/client.py`
Content:
```
1 """Provide a wrapper around epplib to handle authentication and errors."""
2 
3 import logging
4 
5 try:
6     from epplib.client import Client
7     from epplib import commands
8     from epplib.exceptions import TransportError, ParsingError
9     from epplib.transport import SocketTransport
10 except ImportError:
11     pass
12 
13 from django.conf import settings
14 
15 from .cert import Cert, Key
16 from .errors import ErrorCode, LoginError, RegistryError
17 
18 logger = logging.getLogger(__name__)
19 
20 try:
21     # Write cert and key to disk
22     CERT = Cert()
23     KEY = Key()
24 except Exception:
25     CERT = None  # type: ignore
26     KEY = None  # type: ignore
27     logger.warning(
28         "Problem with client certificate. Registrar cannot contact registry.",
29         exc_info=True,
30     )
31 
32 
33 class EPPLibWrapper:
34     """
35     A wrapper over epplib's client.
36 
37     ATTN: This should not be used directly. Use `Domain` from domain.py.
38     """
39 
40     def __init__(self) -> None:
41         """Initialize settings which will be used for all connections."""
42         # set _client to None initially. In the event that the __init__ fails
43         # before _client initializes, app should still start and be in a state
44         # that it can attempt _client initialization on send attempts
45         self._client = None  # type: ignore
46         # prepare (but do not send) a Login command
47         self._login = commands.Login(
48             cl_id=settings.SECRET_REGISTRY_CL_ID,
49             password=settings.SECRET_REGISTRY_PASSWORD,
50             obj_uris=[
51                 "urn:ietf:params:xml:ns:domain-1.0",
52                 "urn:ietf:params:xml:ns:contact-1.0",
53             ],
54         )
55         try:
56             self._initialize_client()
57         except Exception:
58             logger.warning("Unable to configure epplib. Registrar cannot contact registry.")
59 
60     def _initialize_client(self) -> None:
61         """Initialize a client, assuming _login defined. Sets _client to initialized
62         client. Raises errors if initialization fails.
63         This method will be called at app initialization, and also during retries."""
64         # establish a client object with a TCP socket transport
65         # note that type: ignore added in several places because linter complains
66         # about _client initially being set to None, and None type doesn't match code
67         self._client = Client(  # type: ignore
68             SocketTransport(
69                 settings.SECRET_REGISTRY_HOSTNAME,
70                 cert_file=CERT.filename,
71                 key_file=KEY.filename,
72                 password=settings.SECRET_REGISTRY_KEY_PASSPHRASE,
73             )
74         )
75         try:
76             # use the _client object to connect
77             self._client.connect()  # type: ignore
78             response = self._client.send(self._login)  # type: ignore
79             if response.code >= 2000:  # type: ignore
80                 self._client.close()  # type: ignore
81                 raise LoginError(response.msg)  # type: ignore
82         except TransportError as err:
83             message = "_initialize_client failed to execute due to a connection error."
84             logger.error(f"{message} Error: {err}")
85             raise RegistryError(message, code=ErrorCode.TRANSPORT_ERROR) from err
86         except LoginError as err:
87             raise err
88         except Exception as err:
89             message = "_initialize_client failed to execute due to an unknown error."
90             logger.error(f"{message} Error: {err}")
91             raise RegistryError(message) from err
92 
93     def _disconnect(self) -> None:
94         """Close the connection."""
95         try:
96             self._client.send(commands.Logout())  # type: ignore
97             self._client.close()  # type: ignore
98         except Exception:
99             logger.warning("Connection to registry was not cleanly closed.")
100 
101     def _send(self, command):
102         """Helper function used by `send`."""
103         cmd_type = command.__class__.__name__
104 
105         try:
106             # check for the condition that the _client was not initialized properly
107             # at app initialization
108             if self._client is None:
109                 self._initialize_client()
110             response = self._client.send(command)
111         except (ValueError, ParsingError) as err:
112             message = f"{cmd_type} failed to execute due to some syntax error."
113             logger.error(f"{message} Error: {err}")
114             raise RegistryError(message) from err
115         except TransportError as err:
116             message = f"{cmd_type} failed to execute due to a connection error."
117             logger.error(f"{message} Error: {err}")
118             raise RegistryError(message, code=ErrorCode.TRANSPORT_ERROR) from err
119         except LoginError as err:
120             # For linter due to it not liking this line length
121             text = "failed to execute due to a registry login error."
122             message = f"{cmd_type} {text}"
123             logger.error(f"{message} Error: {err}")
124             raise RegistryError(message) from err
125         except Exception as err:
126             message = f"{cmd_type} failed to execute due to an unknown error."
127             logger.error(f"{message} Error: {err}")
128             raise RegistryError(message) from err
129         else:
130             if response.code >= 2000:
131                 raise RegistryError(response.msg, code=response.code)
132             else:
133                 return response
134 
135     def _retry(self, command):
136         """Retry sending a command through EPP by re-initializing the client
137         and then sending the command."""
138         # re-initialize by disconnecting and initial
139         self._disconnect()
140         self._initialize_client()
141         return self._send(command)
142 
143     def send(self, command, *, cleaned=False):
144         """Login, the send the command. Retry once if an error is found"""
145         # try to prevent use of this method without appropriate safeguards
146         cmd_type = command.__class__.__name__
147         if not cleaned:
148             raise ValueError("Please sanitize user input before sending it.")
149         try:
150             return self._send(command)
151         except RegistryError as err:
152             if (
153                 err.is_transport_error()
154                 or err.is_connection_error()
155                 or err.is_session_error()
156                 or err.is_server_error()
157                 or err.should_retry()
158             ):
159                 message = f"{cmd_type} failed and will be retried"
160                 logger.info(f"{message} Error: {err}")
161                 return self._retry(command)
162             else:
163                 raise err
164 
165 
166 try:
167     # Initialize epplib
168     CLIENT = EPPLibWrapper()
169     logger.info("registry client initialized")
170 except Exception:
171     logger.warning("Unable to configure epplib. Registrar cannot contact registry.")
172
```
--- 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/src/epplibwrapper/client.py b/src/epplibwrapper/client.py
--- a/src/epplibwrapper/client.py
+++ b/src/epplibwrapper/client.py
@@ -1,6 +1,7 @@
 """Provide a wrapper around epplib to handle authentication and errors."""
 
 import logging
+from gevent.lock import BoundedSemaphore
 
 try:
     from epplib.client import Client
@@ -52,10 +53,16 @@
                 "urn:ietf:params:xml:ns:contact-1.0",
             ],
         )
+        # We should only ever have one active connection at a time
+        self.connection_lock = BoundedSemaphore(1)
+
+        self.connection_lock.acquire()
         try:
             self._initialize_client()
         except Exception:
-            logger.warning("Unable to configure epplib. Registrar cannot contact registry.")
+            logger.warning("Unable to configure the connection to the registry.")
+        finally:
+            self.connection_lock.release()
 
     def _initialize_client(self) -> None:
         """Initialize a client, assuming _login defined. Sets _client to initialized
@@ -74,11 +81,7 @@
         )
         try:
             # use the _client object to connect
-            self._client.connect()  # type: ignore
-            response = self._client.send(self._login)  # type: ignore
-            if response.code >= 2000:  # type: ignore
-                self._client.close()  # type: ignore
-                raise LoginError(response.msg)  # type: ignore
+            self._connect()
         except TransportError as err:
             message = "_initialize_client failed to execute due to a connection error."
             logger.error(f"{message} Error: {err}")
@@ -90,13 +93,33 @@
             logger.error(f"{message} Error: {err}")
             raise RegistryError(message) from err
 
+    def _connect(self) -> None:
+        """Connects to EPP. Sends a login command. If an invalid response is returned,
+        the client will be closed and a LoginError raised."""
+        self._client.connect()  # type: ignore
+        response = self._client.send(self._login)  # type: ignore
+        if response.code >= 2000:  # type: ignore
+            self._client.close()  # type: ignore
+            raise LoginError(response.msg)  # type: ignore
+
     def _disconnect(self) -> None:
-        """Close the connection."""
+        """Close the connection. Sends a logout command and closes the connection."""
+        self._send_logout_command()
+        self._close_client()
+
+    def _send_logout_command(self):
+        """Sends a logout command to epp"""
         try:
             self._client.send(commands.Logout())  # type: ignore
-            self._client.close()  # type: ignore
-        except Exception:
-            logger.warning("Connection to registry was not cleanly closed.")
+        except Exception as err:
+            logger.warning(f"Logout command not sent successfully: {err}")
+
+    def _close_client(self):
+        """Closes an active client connection"""
+        try:
+            self._client.close()
+        except Exception as err:
+            logger.warning(f"Connection to registry was not cleanly closed: {err}")
 
     def _send(self, command):
         """Helper function used by `send`."""
@@ -146,6 +169,8 @@
         cmd_type = command.__class__.__name__
         if not cleaned:
             raise ValueError("Please sanitize user input before sending it.")
+
+        self.connection_lock.acquire()
         try:
             return self._send(command)
         except RegistryError as err:
@@ -161,6 +186,8 @@
                 return self._retry(command)
             else:
                 raise err
+        finally:
+            self.connection_lock.release()
 
 
 try:

{"golden_diff": "diff --git a/src/epplibwrapper/client.py b/src/epplibwrapper/client.py\n--- a/src/epplibwrapper/client.py\n+++ b/src/epplibwrapper/client.py\n@@ -1,6 +1,7 @@\n \"\"\"Provide a wrapper around epplib to handle authentication and errors.\"\"\"\n \n import logging\n+from gevent.lock import BoundedSemaphore\n \n try:\n     from epplib.client import Client\n@@ -52,10 +53,16 @@\n                 \"urn:ietf:params:xml:ns:contact-1.0\",\n             ],\n         )\n+        # We should only ever have one active connection at a time\n+        self.connection_lock = BoundedSemaphore(1)\n+\n+        self.connection_lock.acquire()\n         try:\n             self._initialize_client()\n         except Exception:\n-            logger.warning(\"Unable to configure epplib. Registrar cannot contact registry.\")\n+            logger.warning(\"Unable to configure the connection to the registry.\")\n+        finally:\n+            self.connection_lock.release()\n \n     def _initialize_client(self) -> None:\n         \"\"\"Initialize a client, assuming _login defined. Sets _client to initialized\n@@ -74,11 +81,7 @@\n         )\n         try:\n             # use the _client object to connect\n-            self._client.connect()  # type: ignore\n-            response = self._client.send(self._login)  # type: ignore\n-            if response.code >= 2000:  # type: ignore\n-                self._client.close()  # type: ignore\n-                raise LoginError(response.msg)  # type: ignore\n+            self._connect()\n         except TransportError as err:\n             message = \"_initialize_client failed to execute due to a connection error.\"\n             logger.error(f\"{message} Error: {err}\")\n@@ -90,13 +93,33 @@\n             logger.error(f\"{message} Error: {err}\")\n             raise RegistryError(message) from err\n \n+    def _connect(self) -> None:\n+        \"\"\"Connects to EPP. Sends a login command. If an invalid response is returned,\n+        the client will be closed and a LoginError raised.\"\"\"\n+        self._client.connect()  # type: ignore\n+        response = self._client.send(self._login)  # type: ignore\n+        if response.code >= 2000:  # type: ignore\n+            self._client.close()  # type: ignore\n+            raise LoginError(response.msg)  # type: ignore\n+\n     def _disconnect(self) -> None:\n-        \"\"\"Close the connection.\"\"\"\n+        \"\"\"Close the connection. Sends a logout command and closes the connection.\"\"\"\n+        self._send_logout_command()\n+        self._close_client()\n+\n+    def _send_logout_command(self):\n+        \"\"\"Sends a logout command to epp\"\"\"\n         try:\n             self._client.send(commands.Logout())  # type: ignore\n-            self._client.close()  # type: ignore\n-        except Exception:\n-            logger.warning(\"Connection to registry was not cleanly closed.\")\n+        except Exception as err:\n+            logger.warning(f\"Logout command not sent successfully: {err}\")\n+\n+    def _close_client(self):\n+        \"\"\"Closes an active client connection\"\"\"\n+        try:\n+            self._client.close()\n+        except Exception as err:\n+            logger.warning(f\"Connection to registry was not cleanly closed: {err}\")\n \n     def _send(self, command):\n         \"\"\"Helper function used by `send`.\"\"\"\n@@ -146,6 +169,8 @@\n         cmd_type = command.__class__.__name__\n         if not cleaned:\n             raise ValueError(\"Please sanitize user input before sending it.\")\n+\n+        self.connection_lock.acquire()\n         try:\n             return self._send(command)\n         except RegistryError as err:\n@@ -161,6 +186,8 @@\n                 return self._retry(command)\n             else:\n                 raise err\n+        finally:\n+            self.connection_lock.release()\n \n \n try:\n", "issue": "Socket in use error when sending epp messages\n### Current Behavior\n\nOccasionally, we are unable to send messages via epp because of the following error:\r\n\"InfoDomain failed to execute due to an unknown error. Error: This socket is already used by another greenlet: <bound method Waiter.switch of <gevent._gevent_c_waiter.Waiter object at 0x7f8e1e5f6750>>\"\n\n### Expected Behavior\n\nNo error should occur.\n\n### Steps to Reproduce\n\nreproduction steps unknown, this happens irregularly and we haven't narrowed down a specif set of easily followed steps that can always guarantee this occurs. see additional context\r\n\n\n### Environment\n\n_No response_\n\n### Additional Context\n\nThis bug started after we added gunicorn using gevent instead of just the default sync option. While we still should be asynchronious this creates an issue with how we are using our sockets on epp calls. We were able to see this more with the removal of the connection pool. \n\n### Issue Links\n\n_No response_\n", "before_files": [{"content": "\"\"\"Provide a wrapper around epplib to handle authentication and errors.\"\"\"\n\nimport logging\n\ntry:\n    from epplib.client import Client\n    from epplib import commands\n    from epplib.exceptions import TransportError, ParsingError\n    from epplib.transport import SocketTransport\nexcept ImportError:\n    pass\n\nfrom django.conf import settings\n\nfrom .cert import Cert, Key\nfrom .errors import ErrorCode, LoginError, RegistryError\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    # Write cert and key to disk\n    CERT = Cert()\n    KEY = Key()\nexcept Exception:\n    CERT = None  # type: ignore\n    KEY = None  # type: ignore\n    logger.warning(\n        \"Problem with client certificate. Registrar cannot contact registry.\",\n        exc_info=True,\n    )\n\n\nclass EPPLibWrapper:\n    \"\"\"\n    A wrapper over epplib's client.\n\n    ATTN: This should not be used directly. Use `Domain` from domain.py.\n    \"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize settings which will be used for all connections.\"\"\"\n        # set _client to None initially. In the event that the __init__ fails\n        # before _client initializes, app should still start and be in a state\n        # that it can attempt _client initialization on send attempts\n        self._client = None  # type: ignore\n        # prepare (but do not send) a Login command\n        self._login = commands.Login(\n            cl_id=settings.SECRET_REGISTRY_CL_ID,\n            password=settings.SECRET_REGISTRY_PASSWORD,\n            obj_uris=[\n                \"urn:ietf:params:xml:ns:domain-1.0\",\n                \"urn:ietf:params:xml:ns:contact-1.0\",\n            ],\n        )\n        try:\n            self._initialize_client()\n        except Exception:\n            logger.warning(\"Unable to configure epplib. Registrar cannot contact registry.\")\n\n    def _initialize_client(self) -> None:\n        \"\"\"Initialize a client, assuming _login defined. Sets _client to initialized\n        client. Raises errors if initialization fails.\n        This method will be called at app initialization, and also during retries.\"\"\"\n        # establish a client object with a TCP socket transport\n        # note that type: ignore added in several places because linter complains\n        # about _client initially being set to None, and None type doesn't match code\n        self._client = Client(  # type: ignore\n            SocketTransport(\n                settings.SECRET_REGISTRY_HOSTNAME,\n                cert_file=CERT.filename,\n                key_file=KEY.filename,\n                password=settings.SECRET_REGISTRY_KEY_PASSPHRASE,\n            )\n        )\n        try:\n            # use the _client object to connect\n            self._client.connect()  # type: ignore\n            response = self._client.send(self._login)  # type: ignore\n            if response.code >= 2000:  # type: ignore\n                self._client.close()  # type: ignore\n                raise LoginError(response.msg)  # type: ignore\n        except TransportError as err:\n            message = \"_initialize_client failed to execute due to a connection error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message, code=ErrorCode.TRANSPORT_ERROR) from err\n        except LoginError as err:\n            raise err\n        except Exception as err:\n            message = \"_initialize_client failed to execute due to an unknown error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message) from err\n\n    def _disconnect(self) -> None:\n        \"\"\"Close the connection.\"\"\"\n        try:\n            self._client.send(commands.Logout())  # type: ignore\n            self._client.close()  # type: ignore\n        except Exception:\n            logger.warning(\"Connection to registry was not cleanly closed.\")\n\n    def _send(self, command):\n        \"\"\"Helper function used by `send`.\"\"\"\n        cmd_type = command.__class__.__name__\n\n        try:\n            # check for the condition that the _client was not initialized properly\n            # at app initialization\n            if self._client is None:\n                self._initialize_client()\n            response = self._client.send(command)\n        except (ValueError, ParsingError) as err:\n            message = f\"{cmd_type} failed to execute due to some syntax error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message) from err\n        except TransportError as err:\n            message = f\"{cmd_type} failed to execute due to a connection error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message, code=ErrorCode.TRANSPORT_ERROR) from err\n        except LoginError as err:\n            # For linter due to it not liking this line length\n            text = \"failed to execute due to a registry login error.\"\n            message = f\"{cmd_type} {text}\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message) from err\n        except Exception as err:\n            message = f\"{cmd_type} failed to execute due to an unknown error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message) from err\n        else:\n            if response.code >= 2000:\n                raise RegistryError(response.msg, code=response.code)\n            else:\n                return response\n\n    def _retry(self, command):\n        \"\"\"Retry sending a command through EPP by re-initializing the client\n        and then sending the command.\"\"\"\n        # re-initialize by disconnecting and initial\n        self._disconnect()\n        self._initialize_client()\n        return self._send(command)\n\n    def send(self, command, *, cleaned=False):\n        \"\"\"Login, the send the command. Retry once if an error is found\"\"\"\n        # try to prevent use of this method without appropriate safeguards\n        cmd_type = command.__class__.__name__\n        if not cleaned:\n            raise ValueError(\"Please sanitize user input before sending it.\")\n        try:\n            return self._send(command)\n        except RegistryError as err:\n            if (\n                err.is_transport_error()\n                or err.is_connection_error()\n                or err.is_session_error()\n                or err.is_server_error()\n                or err.should_retry()\n            ):\n                message = f\"{cmd_type} failed and will be retried\"\n                logger.info(f\"{message} Error: {err}\")\n                return self._retry(command)\n            else:\n                raise err\n\n\ntry:\n    # Initialize epplib\n    CLIENT = EPPLibWrapper()\n    logger.info(\"registry client initialized\")\nexcept Exception:\n    logger.warning(\"Unable to configure epplib. Registrar cannot contact registry.\")\n", "path": "src/epplibwrapper/client.py"}], "after_files": [{"content": "\"\"\"Provide a wrapper around epplib to handle authentication and errors.\"\"\"\n\nimport logging\nfrom gevent.lock import BoundedSemaphore\n\ntry:\n    from epplib.client import Client\n    from epplib import commands\n    from epplib.exceptions import TransportError, ParsingError\n    from epplib.transport import SocketTransport\nexcept ImportError:\n    pass\n\nfrom django.conf import settings\n\nfrom .cert import Cert, Key\nfrom .errors import ErrorCode, LoginError, RegistryError\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    # Write cert and key to disk\n    CERT = Cert()\n    KEY = Key()\nexcept Exception:\n    CERT = None  # type: ignore\n    KEY = None  # type: ignore\n    logger.warning(\n        \"Problem with client certificate. Registrar cannot contact registry.\",\n        exc_info=True,\n    )\n\n\nclass EPPLibWrapper:\n    \"\"\"\n    A wrapper over epplib's client.\n\n    ATTN: This should not be used directly. Use `Domain` from domain.py.\n    \"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize settings which will be used for all connections.\"\"\"\n        # set _client to None initially. In the event that the __init__ fails\n        # before _client initializes, app should still start and be in a state\n        # that it can attempt _client initialization on send attempts\n        self._client = None  # type: ignore\n        # prepare (but do not send) a Login command\n        self._login = commands.Login(\n            cl_id=settings.SECRET_REGISTRY_CL_ID,\n            password=settings.SECRET_REGISTRY_PASSWORD,\n            obj_uris=[\n                \"urn:ietf:params:xml:ns:domain-1.0\",\n                \"urn:ietf:params:xml:ns:contact-1.0\",\n            ],\n        )\n        # We should only ever have one active connection at a time\n        self.connection_lock = BoundedSemaphore(1)\n\n        self.connection_lock.acquire()\n        try:\n            self._initialize_client()\n        except Exception:\n            logger.warning(\"Unable to configure the connection to the registry.\")\n        finally:\n            self.connection_lock.release()\n\n    def _initialize_client(self) -> None:\n        \"\"\"Initialize a client, assuming _login defined. Sets _client to initialized\n        client. Raises errors if initialization fails.\n        This method will be called at app initialization, and also during retries.\"\"\"\n        # establish a client object with a TCP socket transport\n        # note that type: ignore added in several places because linter complains\n        # about _client initially being set to None, and None type doesn't match code\n        self._client = Client(  # type: ignore\n            SocketTransport(\n                settings.SECRET_REGISTRY_HOSTNAME,\n                cert_file=CERT.filename,\n                key_file=KEY.filename,\n                password=settings.SECRET_REGISTRY_KEY_PASSPHRASE,\n            )\n        )\n        try:\n            # use the _client object to connect\n            self._connect()\n        except TransportError as err:\n            message = \"_initialize_client failed to execute due to a connection error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message, code=ErrorCode.TRANSPORT_ERROR) from err\n        except LoginError as err:\n            raise err\n        except Exception as err:\n            message = \"_initialize_client failed to execute due to an unknown error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message) from err\n\n    def _connect(self) -> None:\n        \"\"\"Connects to EPP. Sends a login command. If an invalid response is returned,\n        the client will be closed and a LoginError raised.\"\"\"\n        self._client.connect()  # type: ignore\n        response = self._client.send(self._login)  # type: ignore\n        if response.code >= 2000:  # type: ignore\n            self._client.close()  # type: ignore\n            raise LoginError(response.msg)  # type: ignore\n\n    def _disconnect(self) -> None:\n        \"\"\"Close the connection. Sends a logout command and closes the connection.\"\"\"\n        self._send_logout_command()\n        self._close_client()\n\n    def _send_logout_command(self):\n        \"\"\"Sends a logout command to epp\"\"\"\n        try:\n            self._client.send(commands.Logout())  # type: ignore\n        except Exception as err:\n            logger.warning(f\"Logout command not sent successfully: {err}\")\n\n    def _close_client(self):\n        \"\"\"Closes an active client connection\"\"\"\n        try:\n            self._client.close()\n        except Exception as err:\n            logger.warning(f\"Connection to registry was not cleanly closed: {err}\")\n\n    def _send(self, command):\n        \"\"\"Helper function used by `send`.\"\"\"\n        cmd_type = command.__class__.__name__\n\n        try:\n            # check for the condition that the _client was not initialized properly\n            # at app initialization\n            if self._client is None:\n                self._initialize_client()\n            response = self._client.send(command)\n        except (ValueError, ParsingError) as err:\n            message = f\"{cmd_type} failed to execute due to some syntax error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message) from err\n        except TransportError as err:\n            message = f\"{cmd_type} failed to execute due to a connection error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message, code=ErrorCode.TRANSPORT_ERROR) from err\n        except LoginError as err:\n            # For linter due to it not liking this line length\n            text = \"failed to execute due to a registry login error.\"\n            message = f\"{cmd_type} {text}\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message) from err\n        except Exception as err:\n            message = f\"{cmd_type} failed to execute due to an unknown error.\"\n            logger.error(f\"{message} Error: {err}\")\n            raise RegistryError(message) from err\n        else:\n            if response.code >= 2000:\n                raise RegistryError(response.msg, code=response.code)\n            else:\n                return response\n\n    def _retry(self, command):\n        \"\"\"Retry sending a command through EPP by re-initializing the client\n        and then sending the command.\"\"\"\n        # re-initialize by disconnecting and initial\n        self._disconnect()\n        self._initialize_client()\n        return self._send(command)\n\n    def send(self, command, *, cleaned=False):\n        \"\"\"Login, the send the command. Retry once if an error is found\"\"\"\n        # try to prevent use of this method without appropriate safeguards\n        cmd_type = command.__class__.__name__\n        if not cleaned:\n            raise ValueError(\"Please sanitize user input before sending it.\")\n\n        self.connection_lock.acquire()\n        try:\n            return self._send(command)\n        except RegistryError as err:\n            if (\n                err.is_transport_error()\n                or err.is_connection_error()\n                or err.is_session_error()\n                or err.is_server_error()\n                or err.should_retry()\n            ):\n                message = f\"{cmd_type} failed and will be retried\"\n                logger.info(f\"{message} Error: {err}\")\n                return self._retry(command)\n            else:\n                raise err\n        finally:\n            self.connection_lock.release()\n\n\ntry:\n    # Initialize epplib\n    CLIENT = EPPLibWrapper()\n    logger.info(\"registry client initialized\")\nexcept Exception:\n    logger.warning(\"Unable to configure epplib. Registrar cannot contact registry.\")\n", "path": "src/epplibwrapper/client.py"}]}