We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
logging.exception("Failed to create socket connection!") results in double stracktrace
In jsonrpc_client_base:

        try:
            self._conn = socket.create_connection(('127.0.0.1', self.host_port),
                                                  _SOCKET_TIMEOUT)
            self._conn.settimeout(_SOCKET_TIMEOUT)
        except (socket.timeout, socket.error, IOError):
            logging.exception("Failed to create socket connection!")
            raise

Would result in the trace being printed twice.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mobly/controllers/android_device_lib/jsonrpc_client_base.py`
Content:
```
1 #/usr/bin/env python3.4
2 #
3 # Copyright 2016 Google Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 """Base class for clients that communicate with apps over a JSON RPC interface.
18 
19 The JSON protocol expected by this module is:
20 
21 Request:
22 {
23     "id": <monotonically increasing integer containing the ID of this request>
24     "method": <string containing the name of the method to execute>
25     "params": <JSON array containing the arguments to the method>
26 }
27 
28 Response:
29 {
30     "id": <int id of request that this response maps to>,
31     "result": <Arbitrary JSON object containing the result of executing the
32                method. If the method could not be executed or returned void,
33                contains 'null'.>,
34     "error": <String containing the error thrown by executing the method.
35               If no error occurred, contains 'null'.>
36 }
37 """
38 
39 from builtins import str
40 
41 import json
42 import logging
43 import socket
44 import threading
45 import time
46 
47 # Maximum time to wait for the app to start on the device.
48 APP_START_WAIT_TIME = 15
49 
50 # UID of the 'unknown' jsonrpc session. Will cause creation of a new session.
51 UNKNOWN_UID = -1
52 
53 # Maximum time to wait for the socket to open on the device.
54 _SOCKET_TIMEOUT = 60
55 
56 
57 class Error(Exception):
58     pass
59 
60 
61 class AppStartError(Error):
62     """Raised when the app is not able to be started."""
63 
64 
65 class ApiError(Error):
66     """Raised when remote API reports an error."""
67 
68 
69 class ProtocolError(Error):
70     """Raised when there is some error in exchanging data with server."""
71     NO_RESPONSE_FROM_HANDSHAKE = "No response from handshake."
72     NO_RESPONSE_FROM_SERVER = "No response from server."
73     MISMATCHED_API_ID = "Mismatched API id."
74 
75 
76 class JsonRpcCommand(object):
77     """Commands that can be invoked on all jsonrpc clients.
78 
79     INIT: Initializes a new session.
80     CONTINUE: Creates a connection.
81     """
82     INIT = 'initiate'
83     CONTINUE = 'continue'
84 
85 
86 class JsonRpcClientBase(object):
87     """Base class for jsonrpc clients that connect to remote servers.
88 
89     Connects to a remote device running a jsonrpc-compatible app. Before opening
90     a connection a port forward must be setup to go over usb. This be done using
91     adb.tcp_forward(). This calls the shell command adb forward <local> remote>.
92     Once the port has been forwarded it can be used in this object as the port
93     of communication.
94 
95     Attributes:
96         host_port: (int) The host port of this RPC client.
97         device_port: (int) The device port of this RPC client.
98         app_name: (str) The user-visible name of the app being communicated
99                   with.
100         uid: (int) The uid of this session.
101     """
102 
103     def __init__(self, host_port, device_port, app_name, adb_proxy):
104         """
105         Args:
106             host_port: (int) The host port of this RPC client.
107             device_port: (int) The device port of this RPC client.
108             app_name: (str) The user-visible name of the app being communicated
109                       with.
110             adb_proxy: (adb.AdbProxy) The adb proxy to use to start the app.
111         """
112         self.host_port = host_port
113         self.device_port = device_port
114         self.app_name = app_name
115         self.uid = None
116         self._adb = adb_proxy
117         self._client = None  # prevent close errors on connect failure
118         self._conn = None
119         self._counter = None
120         self._lock = threading.Lock()
121 
122     def __del__(self):
123         self.close()
124 
125     # Methods to be implemented by subclasses.
126 
127     def _do_start_app(self):
128         """Starts the server app on the android device.
129 
130         Must be implemented by subclasses.
131         """
132         raise NotImplementedError()
133 
134     def stop_app(self):
135         """Kills any running instance of the app.
136 
137         Must be implemented by subclasses.
138         """
139         raise NotImplementedError()
140 
141     def _is_app_installed(self):
142         """Checks if app is installed.
143 
144         Must be implemented by subclasses.
145 
146         Returns:
147             True if installed, False otherwise.
148         """
149         raise NotImplementedError()
150 
151     # Rest of the client methods.
152 
153     def check_app_installed(self):
154       if not self._is_app_installed():
155             raise AppStartError(
156                 '%s is not installed on %s' % (self.app_name, self._adb.serial))
157 
158     def start_app(self, wait_time=APP_START_WAIT_TIME):
159         """Starts the server app on the android device.
160 
161         Args:
162             wait_time: float, The time to wait for the app to come up before
163                        raising an error.
164 
165         Raises:
166             AppStartError: When the app was not able to be started.
167         """
168         self.check_app_installed()
169         self._do_start_app()
170         for _ in range(wait_time):
171             time.sleep(1)
172             if self._is_app_running():
173                 return
174         raise AppStartError(
175             '%s failed to start on %s.' % (self.app_name, self._adb.serial))
176 
177     def connect(self, uid=UNKNOWN_UID, cmd=JsonRpcCommand.INIT):
178         """Opens a connection to a JSON RPC server.
179 
180         Opens a connection to a remote client. The connection attempt will time
181         out if it takes longer than _SOCKET_TIMEOUT seconds. Each subsequent
182         operation over this socket will time out after _SOCKET_TIMEOUT seconds
183         as well.
184 
185         Args:
186             uid: int, The uid of the session to join, or UNKNOWN_UID to start a
187                  new session.
188             cmd: JsonRpcCommand, The command to use for creating the connection.
189 
190         Raises:
191             IOError: Raised when the socket times out from io error
192             socket.timeout: Raised when the socket waits to long for connection.
193             ProtocolError: Raised when there is an error in the protocol.
194         """
195         self._counter = self._id_counter()
196         try:
197             self._conn = socket.create_connection(('127.0.0.1', self.host_port),
198                                                   _SOCKET_TIMEOUT)
199             self._conn.settimeout(_SOCKET_TIMEOUT)
200         except (socket.timeout, socket.error, IOError):
201             logging.exception("Failed to create socket connection!")
202             raise
203         self._client = self._conn.makefile(mode="brw")
204 
205         resp = self._cmd(cmd, uid)
206         if not resp:
207             raise ProtocolError(ProtocolError.NO_RESPONSE_FROM_HANDSHAKE)
208         result = json.loads(str(resp, encoding="utf8"))
209         if result['status']:
210             self.uid = result['uid']
211         else:
212             self.uid = UNKNOWN_UID
213 
214     def close(self):
215         """Close the connection to the remote client."""
216         if self._conn:
217             self._conn.close()
218             self._conn = None
219 
220     def _cmd(self, command, uid=None):
221         """Send a command to the server.
222 
223         Args:
224             command: str, The name of the command to execute.
225             uid: int, the uid of the session to send the command to.
226 
227         Returns:
228             The line that was written back.
229         """
230         if not uid:
231             uid = self.uid
232         self._client.write(
233             json.dumps({'cmd': command, 'uid': uid}).encode("utf8") + b'\n')
234         self._client.flush()
235         return self._client.readline()
236 
237     def _rpc(self, method, *args):
238         """Sends an rpc to the app.
239 
240         Args:
241             method: str, The name of the method to execute.
242             args: any, The args of the method.
243 
244         Returns:
245             The result of the rpc.
246 
247         Raises:
248             ProtocolError: Something went wrong with the protocol.
249             ApiError: The rpc went through, however executed with errors.
250         """
251         with self._lock:
252             apiid = next(self._counter)
253         data = {'id': apiid, 'method': method, 'params': args}
254         request = json.dumps(data)
255         self._client.write(request.encode("utf8") + b'\n')
256         self._client.flush()
257         response = self._client.readline()
258         if not response:
259             raise ProtocolError(ProtocolError.NO_RESPONSE_FROM_SERVER)
260         result = json.loads(str(response, encoding="utf8"))
261         if result['error']:
262             raise ApiError(result['error'])
263         if result['id'] != apiid:
264             raise ProtocolError(ProtocolError.MISMATCHED_API_ID)
265         return result['result']
266 
267     def _is_app_running(self):
268         """Checks if the app is currently running on an android device.
269 
270         May be overridden by subclasses with custom sanity checks.
271         """
272         running = False
273         try:
274           self.connect()
275           running = True
276         finally:
277           self.close()
278           # This 'return' squashes exceptions from connect()
279           return running
280 
281     def __getattr__(self, name):
282         """Wrapper for python magic to turn method calls into RPC calls."""
283         def rpc_call(*args):
284             return self._rpc(name, *args)
285         return rpc_call
286 
287     def _id_counter(self):
288         i = 0
289         while True:
290             yield i
291             i += 1
292
```
--- 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/mobly/controllers/android_device_lib/jsonrpc_client_base.py b/mobly/controllers/android_device_lib/jsonrpc_client_base.py
--- a/mobly/controllers/android_device_lib/jsonrpc_client_base.py
+++ b/mobly/controllers/android_device_lib/jsonrpc_client_base.py
@@ -39,7 +39,6 @@
 from builtins import str
 
 import json
-import logging
 import socket
 import threading
 import time
@@ -193,13 +192,9 @@
             ProtocolError: Raised when there is an error in the protocol.
         """
         self._counter = self._id_counter()
-        try:
-            self._conn = socket.create_connection(('127.0.0.1', self.host_port),
-                                                  _SOCKET_TIMEOUT)
-            self._conn.settimeout(_SOCKET_TIMEOUT)
-        except (socket.timeout, socket.error, IOError):
-            logging.exception("Failed to create socket connection!")
-            raise
+        self._conn = socket.create_connection(('127.0.0.1', self.host_port),
+                                              _SOCKET_TIMEOUT)
+        self._conn.settimeout(_SOCKET_TIMEOUT)
         self._client = self._conn.makefile(mode="brw")
 
         resp = self._cmd(cmd, uid)

{"golden_diff": "diff --git a/mobly/controllers/android_device_lib/jsonrpc_client_base.py b/mobly/controllers/android_device_lib/jsonrpc_client_base.py\n--- a/mobly/controllers/android_device_lib/jsonrpc_client_base.py\n+++ b/mobly/controllers/android_device_lib/jsonrpc_client_base.py\n@@ -39,7 +39,6 @@\n from builtins import str\n \n import json\n-import logging\n import socket\n import threading\n import time\n@@ -193,13 +192,9 @@\n             ProtocolError: Raised when there is an error in the protocol.\n         \"\"\"\n         self._counter = self._id_counter()\n-        try:\n-            self._conn = socket.create_connection(('127.0.0.1', self.host_port),\n-                                                  _SOCKET_TIMEOUT)\n-            self._conn.settimeout(_SOCKET_TIMEOUT)\n-        except (socket.timeout, socket.error, IOError):\n-            logging.exception(\"Failed to create socket connection!\")\n-            raise\n+        self._conn = socket.create_connection(('127.0.0.1', self.host_port),\n+                                              _SOCKET_TIMEOUT)\n+        self._conn.settimeout(_SOCKET_TIMEOUT)\n         self._client = self._conn.makefile(mode=\"brw\")\n \n         resp = self._cmd(cmd, uid)\n", "issue": "logging.exception(\"Failed to create socket connection!\") results in double stracktrace\nIn jsonrpc_client_base:\r\n\r\n        try:\r\n            self._conn = socket.create_connection(('127.0.0.1', self.host_port),\r\n                                                  _SOCKET_TIMEOUT)\r\n            self._conn.settimeout(_SOCKET_TIMEOUT)\r\n        except (socket.timeout, socket.error, IOError):\r\n            logging.exception(\"Failed to create socket connection!\")\r\n            raise\r\n\r\nWould result in the trace being printed twice.\n", "before_files": [{"content": "#/usr/bin/env python3.4\n#\n# Copyright 2016 Google Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Base class for clients that communicate with apps over a JSON RPC interface.\n\nThe JSON protocol expected by this module is:\n\nRequest:\n{\n    \"id\": <monotonically increasing integer containing the ID of this request>\n    \"method\": <string containing the name of the method to execute>\n    \"params\": <JSON array containing the arguments to the method>\n}\n\nResponse:\n{\n    \"id\": <int id of request that this response maps to>,\n    \"result\": <Arbitrary JSON object containing the result of executing the\n               method. If the method could not be executed or returned void,\n               contains 'null'.>,\n    \"error\": <String containing the error thrown by executing the method.\n              If no error occurred, contains 'null'.>\n}\n\"\"\"\n\nfrom builtins import str\n\nimport json\nimport logging\nimport socket\nimport threading\nimport time\n\n# Maximum time to wait for the app to start on the device.\nAPP_START_WAIT_TIME = 15\n\n# UID of the 'unknown' jsonrpc session. Will cause creation of a new session.\nUNKNOWN_UID = -1\n\n# Maximum time to wait for the socket to open on the device.\n_SOCKET_TIMEOUT = 60\n\n\nclass Error(Exception):\n    pass\n\n\nclass AppStartError(Error):\n    \"\"\"Raised when the app is not able to be started.\"\"\"\n\n\nclass ApiError(Error):\n    \"\"\"Raised when remote API reports an error.\"\"\"\n\n\nclass ProtocolError(Error):\n    \"\"\"Raised when there is some error in exchanging data with server.\"\"\"\n    NO_RESPONSE_FROM_HANDSHAKE = \"No response from handshake.\"\n    NO_RESPONSE_FROM_SERVER = \"No response from server.\"\n    MISMATCHED_API_ID = \"Mismatched API id.\"\n\n\nclass JsonRpcCommand(object):\n    \"\"\"Commands that can be invoked on all jsonrpc clients.\n\n    INIT: Initializes a new session.\n    CONTINUE: Creates a connection.\n    \"\"\"\n    INIT = 'initiate'\n    CONTINUE = 'continue'\n\n\nclass JsonRpcClientBase(object):\n    \"\"\"Base class for jsonrpc clients that connect to remote servers.\n\n    Connects to a remote device running a jsonrpc-compatible app. Before opening\n    a connection a port forward must be setup to go over usb. This be done using\n    adb.tcp_forward(). This calls the shell command adb forward <local> remote>.\n    Once the port has been forwarded it can be used in this object as the port\n    of communication.\n\n    Attributes:\n        host_port: (int) The host port of this RPC client.\n        device_port: (int) The device port of this RPC client.\n        app_name: (str) The user-visible name of the app being communicated\n                  with.\n        uid: (int) The uid of this session.\n    \"\"\"\n\n    def __init__(self, host_port, device_port, app_name, adb_proxy):\n        \"\"\"\n        Args:\n            host_port: (int) The host port of this RPC client.\n            device_port: (int) The device port of this RPC client.\n            app_name: (str) The user-visible name of the app being communicated\n                      with.\n            adb_proxy: (adb.AdbProxy) The adb proxy to use to start the app.\n        \"\"\"\n        self.host_port = host_port\n        self.device_port = device_port\n        self.app_name = app_name\n        self.uid = None\n        self._adb = adb_proxy\n        self._client = None  # prevent close errors on connect failure\n        self._conn = None\n        self._counter = None\n        self._lock = threading.Lock()\n\n    def __del__(self):\n        self.close()\n\n    # Methods to be implemented by subclasses.\n\n    def _do_start_app(self):\n        \"\"\"Starts the server app on the android device.\n\n        Must be implemented by subclasses.\n        \"\"\"\n        raise NotImplementedError()\n\n    def stop_app(self):\n        \"\"\"Kills any running instance of the app.\n\n        Must be implemented by subclasses.\n        \"\"\"\n        raise NotImplementedError()\n\n    def _is_app_installed(self):\n        \"\"\"Checks if app is installed.\n\n        Must be implemented by subclasses.\n\n        Returns:\n            True if installed, False otherwise.\n        \"\"\"\n        raise NotImplementedError()\n\n    # Rest of the client methods.\n\n    def check_app_installed(self):\n      if not self._is_app_installed():\n            raise AppStartError(\n                '%s is not installed on %s' % (self.app_name, self._adb.serial))\n\n    def start_app(self, wait_time=APP_START_WAIT_TIME):\n        \"\"\"Starts the server app on the android device.\n\n        Args:\n            wait_time: float, The time to wait for the app to come up before\n                       raising an error.\n\n        Raises:\n            AppStartError: When the app was not able to be started.\n        \"\"\"\n        self.check_app_installed()\n        self._do_start_app()\n        for _ in range(wait_time):\n            time.sleep(1)\n            if self._is_app_running():\n                return\n        raise AppStartError(\n            '%s failed to start on %s.' % (self.app_name, self._adb.serial))\n\n    def connect(self, uid=UNKNOWN_UID, cmd=JsonRpcCommand.INIT):\n        \"\"\"Opens a connection to a JSON RPC server.\n\n        Opens a connection to a remote client. The connection attempt will time\n        out if it takes longer than _SOCKET_TIMEOUT seconds. Each subsequent\n        operation over this socket will time out after _SOCKET_TIMEOUT seconds\n        as well.\n\n        Args:\n            uid: int, The uid of the session to join, or UNKNOWN_UID to start a\n                 new session.\n            cmd: JsonRpcCommand, The command to use for creating the connection.\n\n        Raises:\n            IOError: Raised when the socket times out from io error\n            socket.timeout: Raised when the socket waits to long for connection.\n            ProtocolError: Raised when there is an error in the protocol.\n        \"\"\"\n        self._counter = self._id_counter()\n        try:\n            self._conn = socket.create_connection(('127.0.0.1', self.host_port),\n                                                  _SOCKET_TIMEOUT)\n            self._conn.settimeout(_SOCKET_TIMEOUT)\n        except (socket.timeout, socket.error, IOError):\n            logging.exception(\"Failed to create socket connection!\")\n            raise\n        self._client = self._conn.makefile(mode=\"brw\")\n\n        resp = self._cmd(cmd, uid)\n        if not resp:\n            raise ProtocolError(ProtocolError.NO_RESPONSE_FROM_HANDSHAKE)\n        result = json.loads(str(resp, encoding=\"utf8\"))\n        if result['status']:\n            self.uid = result['uid']\n        else:\n            self.uid = UNKNOWN_UID\n\n    def close(self):\n        \"\"\"Close the connection to the remote client.\"\"\"\n        if self._conn:\n            self._conn.close()\n            self._conn = None\n\n    def _cmd(self, command, uid=None):\n        \"\"\"Send a command to the server.\n\n        Args:\n            command: str, The name of the command to execute.\n            uid: int, the uid of the session to send the command to.\n\n        Returns:\n            The line that was written back.\n        \"\"\"\n        if not uid:\n            uid = self.uid\n        self._client.write(\n            json.dumps({'cmd': command, 'uid': uid}).encode(\"utf8\") + b'\\n')\n        self._client.flush()\n        return self._client.readline()\n\n    def _rpc(self, method, *args):\n        \"\"\"Sends an rpc to the app.\n\n        Args:\n            method: str, The name of the method to execute.\n            args: any, The args of the method.\n\n        Returns:\n            The result of the rpc.\n\n        Raises:\n            ProtocolError: Something went wrong with the protocol.\n            ApiError: The rpc went through, however executed with errors.\n        \"\"\"\n        with self._lock:\n            apiid = next(self._counter)\n        data = {'id': apiid, 'method': method, 'params': args}\n        request = json.dumps(data)\n        self._client.write(request.encode(\"utf8\") + b'\\n')\n        self._client.flush()\n        response = self._client.readline()\n        if not response:\n            raise ProtocolError(ProtocolError.NO_RESPONSE_FROM_SERVER)\n        result = json.loads(str(response, encoding=\"utf8\"))\n        if result['error']:\n            raise ApiError(result['error'])\n        if result['id'] != apiid:\n            raise ProtocolError(ProtocolError.MISMATCHED_API_ID)\n        return result['result']\n\n    def _is_app_running(self):\n        \"\"\"Checks if the app is currently running on an android device.\n\n        May be overridden by subclasses with custom sanity checks.\n        \"\"\"\n        running = False\n        try:\n          self.connect()\n          running = True\n        finally:\n          self.close()\n          # This 'return' squashes exceptions from connect()\n          return running\n\n    def __getattr__(self, name):\n        \"\"\"Wrapper for python magic to turn method calls into RPC calls.\"\"\"\n        def rpc_call(*args):\n            return self._rpc(name, *args)\n        return rpc_call\n\n    def _id_counter(self):\n        i = 0\n        while True:\n            yield i\n            i += 1\n", "path": "mobly/controllers/android_device_lib/jsonrpc_client_base.py"}], "after_files": [{"content": "#/usr/bin/env python3.4\n#\n# Copyright 2016 Google Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Base class for clients that communicate with apps over a JSON RPC interface.\n\nThe JSON protocol expected by this module is:\n\nRequest:\n{\n    \"id\": <monotonically increasing integer containing the ID of this request>\n    \"method\": <string containing the name of the method to execute>\n    \"params\": <JSON array containing the arguments to the method>\n}\n\nResponse:\n{\n    \"id\": <int id of request that this response maps to>,\n    \"result\": <Arbitrary JSON object containing the result of executing the\n               method. If the method could not be executed or returned void,\n               contains 'null'.>,\n    \"error\": <String containing the error thrown by executing the method.\n              If no error occurred, contains 'null'.>\n}\n\"\"\"\n\nfrom builtins import str\n\nimport json\nimport socket\nimport threading\nimport time\n\n# Maximum time to wait for the app to start on the device.\nAPP_START_WAIT_TIME = 15\n\n# UID of the 'unknown' jsonrpc session. Will cause creation of a new session.\nUNKNOWN_UID = -1\n\n# Maximum time to wait for the socket to open on the device.\n_SOCKET_TIMEOUT = 60\n\n\nclass Error(Exception):\n    pass\n\n\nclass AppStartError(Error):\n    \"\"\"Raised when the app is not able to be started.\"\"\"\n\n\nclass ApiError(Error):\n    \"\"\"Raised when remote API reports an error.\"\"\"\n\n\nclass ProtocolError(Error):\n    \"\"\"Raised when there is some error in exchanging data with server.\"\"\"\n    NO_RESPONSE_FROM_HANDSHAKE = \"No response from handshake.\"\n    NO_RESPONSE_FROM_SERVER = \"No response from server.\"\n    MISMATCHED_API_ID = \"Mismatched API id.\"\n\n\nclass JsonRpcCommand(object):\n    \"\"\"Commands that can be invoked on all jsonrpc clients.\n\n    INIT: Initializes a new session.\n    CONTINUE: Creates a connection.\n    \"\"\"\n    INIT = 'initiate'\n    CONTINUE = 'continue'\n\n\nclass JsonRpcClientBase(object):\n    \"\"\"Base class for jsonrpc clients that connect to remote servers.\n\n    Connects to a remote device running a jsonrpc-compatible app. Before opening\n    a connection a port forward must be setup to go over usb. This be done using\n    adb.tcp_forward(). This calls the shell command adb forward <local> remote>.\n    Once the port has been forwarded it can be used in this object as the port\n    of communication.\n\n    Attributes:\n        host_port: (int) The host port of this RPC client.\n        device_port: (int) The device port of this RPC client.\n        app_name: (str) The user-visible name of the app being communicated\n                  with.\n        uid: (int) The uid of this session.\n    \"\"\"\n\n    def __init__(self, host_port, device_port, app_name, adb_proxy):\n        \"\"\"\n        Args:\n            host_port: (int) The host port of this RPC client.\n            device_port: (int) The device port of this RPC client.\n            app_name: (str) The user-visible name of the app being communicated\n                      with.\n            adb_proxy: (adb.AdbProxy) The adb proxy to use to start the app.\n        \"\"\"\n        self.host_port = host_port\n        self.device_port = device_port\n        self.app_name = app_name\n        self.uid = None\n        self._adb = adb_proxy\n        self._client = None  # prevent close errors on connect failure\n        self._conn = None\n        self._counter = None\n        self._lock = threading.Lock()\n\n    def __del__(self):\n        self.close()\n\n    # Methods to be implemented by subclasses.\n\n    def _do_start_app(self):\n        \"\"\"Starts the server app on the android device.\n\n        Must be implemented by subclasses.\n        \"\"\"\n        raise NotImplementedError()\n\n    def stop_app(self):\n        \"\"\"Kills any running instance of the app.\n\n        Must be implemented by subclasses.\n        \"\"\"\n        raise NotImplementedError()\n\n    def _is_app_installed(self):\n        \"\"\"Checks if app is installed.\n\n        Must be implemented by subclasses.\n\n        Returns:\n            True if installed, False otherwise.\n        \"\"\"\n        raise NotImplementedError()\n\n    # Rest of the client methods.\n\n    def check_app_installed(self):\n      if not self._is_app_installed():\n            raise AppStartError(\n                '%s is not installed on %s' % (self.app_name, self._adb.serial))\n\n    def start_app(self, wait_time=APP_START_WAIT_TIME):\n        \"\"\"Starts the server app on the android device.\n\n        Args:\n            wait_time: float, The time to wait for the app to come up before\n                       raising an error.\n\n        Raises:\n            AppStartError: When the app was not able to be started.\n        \"\"\"\n        self.check_app_installed()\n        self._do_start_app()\n        for _ in range(wait_time):\n            time.sleep(1)\n            if self._is_app_running():\n                return\n        raise AppStartError(\n            '%s failed to start on %s.' % (self.app_name, self._adb.serial))\n\n    def connect(self, uid=UNKNOWN_UID, cmd=JsonRpcCommand.INIT):\n        \"\"\"Opens a connection to a JSON RPC server.\n\n        Opens a connection to a remote client. The connection attempt will time\n        out if it takes longer than _SOCKET_TIMEOUT seconds. Each subsequent\n        operation over this socket will time out after _SOCKET_TIMEOUT seconds\n        as well.\n\n        Args:\n            uid: int, The uid of the session to join, or UNKNOWN_UID to start a\n                 new session.\n            cmd: JsonRpcCommand, The command to use for creating the connection.\n\n        Raises:\n            IOError: Raised when the socket times out from io error\n            socket.timeout: Raised when the socket waits to long for connection.\n            ProtocolError: Raised when there is an error in the protocol.\n        \"\"\"\n        self._counter = self._id_counter()\n        self._conn = socket.create_connection(('127.0.0.1', self.host_port),\n                                              _SOCKET_TIMEOUT)\n        self._conn.settimeout(_SOCKET_TIMEOUT)\n        self._client = self._conn.makefile(mode=\"brw\")\n\n        resp = self._cmd(cmd, uid)\n        if not resp:\n            raise ProtocolError(ProtocolError.NO_RESPONSE_FROM_HANDSHAKE)\n        result = json.loads(str(resp, encoding=\"utf8\"))\n        if result['status']:\n            self.uid = result['uid']\n        else:\n            self.uid = UNKNOWN_UID\n\n    def close(self):\n        \"\"\"Close the connection to the remote client.\"\"\"\n        if self._conn:\n            self._conn.close()\n            self._conn = None\n\n    def _cmd(self, command, uid=None):\n        \"\"\"Send a command to the server.\n\n        Args:\n            command: str, The name of the command to execute.\n            uid: int, the uid of the session to send the command to.\n\n        Returns:\n            The line that was written back.\n        \"\"\"\n        if not uid:\n            uid = self.uid\n        self._client.write(\n            json.dumps({'cmd': command, 'uid': uid}).encode(\"utf8\") + b'\\n')\n        self._client.flush()\n        return self._client.readline()\n\n    def _rpc(self, method, *args):\n        \"\"\"Sends an rpc to the app.\n\n        Args:\n            method: str, The name of the method to execute.\n            args: any, The args of the method.\n\n        Returns:\n            The result of the rpc.\n\n        Raises:\n            ProtocolError: Something went wrong with the protocol.\n            ApiError: The rpc went through, however executed with errors.\n        \"\"\"\n        with self._lock:\n            apiid = next(self._counter)\n        data = {'id': apiid, 'method': method, 'params': args}\n        request = json.dumps(data)\n        self._client.write(request.encode(\"utf8\") + b'\\n')\n        self._client.flush()\n        response = self._client.readline()\n        if not response:\n            raise ProtocolError(ProtocolError.NO_RESPONSE_FROM_SERVER)\n        result = json.loads(str(response, encoding=\"utf8\"))\n        if result['error']:\n            raise ApiError(result['error'])\n        if result['id'] != apiid:\n            raise ProtocolError(ProtocolError.MISMATCHED_API_ID)\n        return result['result']\n\n    def _is_app_running(self):\n        \"\"\"Checks if the app is currently running on an android device.\n\n        May be overridden by subclasses with custom sanity checks.\n        \"\"\"\n        running = False\n        try:\n          self.connect()\n          running = True\n        finally:\n          self.close()\n          # This 'return' squashes exceptions from connect()\n          return running\n\n    def __getattr__(self, name):\n        \"\"\"Wrapper for python magic to turn method calls into RPC calls.\"\"\"\n        def rpc_call(*args):\n            return self._rpc(name, *args)\n        return rpc_call\n\n    def _id_counter(self):\n        i = 0\n        while True:\n            yield i\n            i += 1\n", "path": "mobly/controllers/android_device_lib/jsonrpc_client_base.py"}]}