We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cannot exit ipython with running qcodes.Monitor()
### Steps to reproduce
iPython version 8.10, qcodes version 0.42, Windows 10 Pro 22H2

1. open ipython
2. from qcodes import Monitor
monitor = Monitor()
3. exit ipython

It hangs instead of closing iPython. The terminal must be terminated.


--- 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/qcodes/monitor/monitor.py`
Content:
```
1 #! /usr/bin/env python
2 # vim:fenc=utf-8
3 #
4 # Copyright © 2017 unga <giulioungaretti@me.com>
5 #
6 # Distributed under terms of the MIT license.
7 """
8 Monitor a set of parameters in a background thread
9 stream output over websocket
10 
11 To start monitor, run this file, or if qcodes is installed as a module:
12 
13 ``% python -m qcodes.monitor.monitor``
14 
15 Add parameters to monitor in your measurement by creating a new monitor with a
16 list of parameters to monitor:
17 
18 ``monitor = qcodes.Monitor(param1, param2, param3, ...)``
19 """
20 from __future__ import annotations
21 
22 import asyncio
23 import json
24 import logging
25 import os
26 import socketserver
27 import time
28 import webbrowser
29 from asyncio import CancelledError
30 from collections import defaultdict
31 from collections.abc import Awaitable, Sequence
32 from contextlib import suppress
33 from importlib.resources import as_file, files
34 from threading import Event, Thread
35 from typing import Any, Callable
36 
37 import websockets
38 import websockets.exceptions
39 import websockets.server
40 
41 from qcodes.parameters import Parameter
42 
43 WEBSOCKET_PORT = 5678
44 SERVER_PORT = 3000
45 
46 log = logging.getLogger(__name__)
47 
48 
49 def _get_metadata(
50     *parameters: Parameter, use_root_instrument: bool = True
51 ) -> dict[str, Any]:
52     """
53     Return a dictionary that contains the parameter metadata grouped by the
54     instrument it belongs to.
55     """
56     metadata_timestamp = time.time()
57     # group metadata by instrument
58     metas: dict[Any, Any] = defaultdict(list)
59     for parameter in parameters:
60         # Get the latest value from the parameter,
61         # respecting the max_val_age parameter
62         meta: dict[str, float | str | None] = {}
63         meta["value"] = str(parameter.get_latest())
64         timestamp = parameter.get_latest.get_timestamp()
65         if timestamp is not None:
66             meta["ts"] = timestamp.timestamp()
67         else:
68             meta["ts"] = None
69         meta["name"] = parameter.label or parameter.name
70         meta["unit"] = parameter.unit
71 
72         # find the base instrument that this parameter belongs to
73         if use_root_instrument:
74             baseinst = parameter.root_instrument
75         else:
76             baseinst = parameter.instrument
77         if baseinst is None:
78             metas["Unbound Parameter"].append(meta)
79         else:
80             metas[str(baseinst)].append(meta)
81 
82     # Create list of parameters, grouped by instrument
83     parameters_out = []
84     for instrument in metas:
85         temp = {"instrument": instrument, "parameters": metas[instrument]}
86         parameters_out.append(temp)
87 
88     state = {"ts": metadata_timestamp, "parameters": parameters_out}
89     return state
90 
91 
92 def _handler(
93     parameters: Sequence[Parameter], interval: float, use_root_instrument: bool = True
94 ) -> Callable[[websockets.server.WebSocketServerProtocol, str], Awaitable[None]]:
95     """
96     Return the websockets server handler.
97     """
98 
99     async def server_func(
100         websocket: websockets.server.WebSocketServerProtocol, _: str
101     ) -> None:
102         """
103         Create a websockets handler that sends parameter values to a listener
104         every "interval" seconds.
105         """
106         while True:
107             try:
108                 # Update the parameter values
109                 try:
110                     meta = _get_metadata(
111                         *parameters, use_root_instrument=use_root_instrument
112                     )
113                 except ValueError:
114                     log.exception("Error getting parameters")
115                     break
116                 log.debug("sending.. to %r", websocket)
117                 await websocket.send(json.dumps(meta))
118                 # Wait for interval seconds and then send again
119                 await asyncio.sleep(interval)
120             except (CancelledError, websockets.exceptions.ConnectionClosed):
121                 log.debug("Got CancelledError or ConnectionClosed",
122                           exc_info=True)
123                 break
124         log.debug("Closing websockets connection")
125 
126     return server_func
127 
128 
129 class Monitor(Thread):
130     """
131     QCodes Monitor - WebSockets server to monitor qcodes parameters.
132     """
133     running = None
134 
135     def __init__(
136         self,
137         *parameters: Parameter,
138         interval: float = 1,
139         use_root_instrument: bool = True,
140     ):
141         """
142         Monitor qcodes parameters.
143 
144         Args:
145             *parameters: Parameters to monitor.
146             interval: How often one wants to refresh the values.
147             use_root_instrument: Defines if parameters are grouped according to
148                                 parameter.root_instrument or parameter.instrument
149         """
150         super().__init__()
151 
152         # Check that all values are valid parameters
153         for parameter in parameters:
154             if not isinstance(parameter, Parameter):
155                 raise TypeError(f"We can only monitor QCodes "
156                                 f"Parameters, not {type(parameter)}")
157 
158         self.loop: asyncio.AbstractEventLoop | None = None
159         self._stop_loop_future: asyncio.Future | None = None
160         self._parameters = parameters
161         self.loop_is_closed = Event()
162         self.server_is_started = Event()
163         self.handler = _handler(
164             parameters, interval=interval, use_root_instrument=use_root_instrument
165         )
166         log.debug("Start monitoring thread")
167         if Monitor.running:
168             # stop the old server
169             log.debug("Stopping and restarting server")
170             Monitor.running.stop()
171         self.start()
172 
173         # Wait until the loop is running
174         self.server_is_started.wait(timeout=5)
175         if not self.server_is_started.is_set():
176             raise RuntimeError("Failed to start server")
177         Monitor.running = self
178 
179     def run(self) -> None:
180         """
181         Start the event loop and run forever.
182         """
183         log.debug("Running Websocket server")
184 
185         async def run_loop() -> None:
186             self.loop = asyncio.get_running_loop()
187             self._stop_loop_future = self.loop.create_future()
188 
189             async with websockets.server.serve(
190                 self.handler, "127.0.0.1", WEBSOCKET_PORT, close_timeout=1
191             ):
192                 self.server_is_started.set()
193                 try:
194                     await self._stop_loop_future
195                 except asyncio.CancelledError:
196                     log.debug("Websocket server thread shutting down")
197 
198         try:
199             asyncio.run(run_loop())
200         finally:
201             self.loop_is_closed.set()
202 
203     def update_all(self) -> None:
204         """
205         Update all parameters in the monitor.
206         """
207         for parameter in self._parameters:
208             # call get if it can be called without arguments
209             with suppress(TypeError):
210                 parameter.get()
211 
212     def stop(self) -> None:
213         """
214         Shutdown the server, close the event loop and join the thread.
215         Setting active Monitor to ``None``.
216         """
217         self.join()
218         Monitor.running = None
219 
220     def join(self, timeout: float | None = None) -> None:
221         """
222         Overwrite ``Thread.join`` to make sure server is stopped before
223         joining avoiding a potential deadlock.
224         """
225         log.debug("Shutting down server")
226         if not self.is_alive():
227             # we run this check before trying to run to prevent a cryptic
228             # error message
229             log.debug("monitor is dead")
230             return
231         try:
232             if self.loop is not None and self._stop_loop_future is not None:
233                 log.debug("Instructing server to stop event loop.")
234                 self.loop.call_soon_threadsafe(self._stop_loop_future.cancel)
235             else:
236                 log.debug("No event loop found. Cannot stop event loop.")
237         except RuntimeError:
238             # the above may throw a runtime error if the loop is already
239             # stopped in which case there is nothing more to do
240             log.exception("Could not close loop")
241         self.loop_is_closed.wait(timeout=5)
242         if not self.loop_is_closed.is_set():
243             raise RuntimeError("Failed to join loop")
244         log.debug("Loop reported closed")
245         super().join(timeout=timeout)
246         log.debug("Monitor Thread has joined")
247 
248     @staticmethod
249     def show() -> None:
250         """
251         Overwrite this method to show/raise your monitor GUI
252         F.ex.
253 
254         ::
255 
256             import webbrowser
257             url = "localhost:3000"
258             # Open URL in new window, raising the window if possible.
259             webbrowser.open_new(url)
260 
261         """
262         webbrowser.open(f"http://localhost:{SERVER_PORT}")
263 
264 
265 def main() -> None:
266     import http.server
267 
268     # If this file is run, create a simple webserver that serves a simple
269     # website that can be used to view monitored parameters.
270     # # https://github.com/python/mypy/issues/4182
271     parent_module = ".".join(__loader__.name.split(".")[:-1])  # type: ignore[name-defined]
272 
273     static_dir = files(parent_module).joinpath("dist")
274     try:
275         with as_file(static_dir) as extracted_dir:
276             os.chdir(extracted_dir)
277             log.info("Starting HTTP Server at http://localhost:%i", SERVER_PORT)
278             with socketserver.TCPServer(
279                 ("", SERVER_PORT), http.server.SimpleHTTPRequestHandler
280             ) as httpd:
281                 log.debug("serving directory %s", static_dir)
282                 webbrowser.open(f"http://localhost:{SERVER_PORT}")
283                 httpd.serve_forever()
284     except KeyboardInterrupt:
285         log.info("Shutting Down HTTP Server")
286 
287 
288 if __name__ == "__main__":
289     main()
290
```
--- 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/qcodes/monitor/monitor.py b/src/qcodes/monitor/monitor.py
--- a/src/qcodes/monitor/monitor.py
+++ b/src/qcodes/monitor/monitor.py
@@ -147,7 +147,7 @@
             use_root_instrument: Defines if parameters are grouped according to
                                 parameter.root_instrument or parameter.instrument
         """
-        super().__init__()
+        super().__init__(daemon=True)
 
         # Check that all values are valid parameters
         for parameter in parameters:

{"golden_diff": "diff --git a/src/qcodes/monitor/monitor.py b/src/qcodes/monitor/monitor.py\n--- a/src/qcodes/monitor/monitor.py\n+++ b/src/qcodes/monitor/monitor.py\n@@ -147,7 +147,7 @@\n             use_root_instrument: Defines if parameters are grouped according to\n                                 parameter.root_instrument or parameter.instrument\n         \"\"\"\n-        super().__init__()\n+        super().__init__(daemon=True)\n \n         # Check that all values are valid parameters\n         for parameter in parameters:\n", "issue": "Cannot exit ipython with running qcodes.Monitor()\n### Steps to reproduce\r\niPython version 8.10, qcodes version 0.42, Windows 10 Pro 22H2\r\n\r\n1. open ipython\r\n2. from qcodes import Monitor\r\nmonitor = Monitor()\r\n3. exit ipython\r\n\r\nIt hangs instead of closing iPython. The terminal must be terminated.\r\n\n", "before_files": [{"content": "#! /usr/bin/env python\n# vim:fenc=utf-8\n#\n# Copyright \u00a9 2017 unga <giulioungaretti@me.com>\n#\n# Distributed under terms of the MIT license.\n\"\"\"\nMonitor a set of parameters in a background thread\nstream output over websocket\n\nTo start monitor, run this file, or if qcodes is installed as a module:\n\n``% python -m qcodes.monitor.monitor``\n\nAdd parameters to monitor in your measurement by creating a new monitor with a\nlist of parameters to monitor:\n\n``monitor = qcodes.Monitor(param1, param2, param3, ...)``\n\"\"\"\nfrom __future__ import annotations\n\nimport asyncio\nimport json\nimport logging\nimport os\nimport socketserver\nimport time\nimport webbrowser\nfrom asyncio import CancelledError\nfrom collections import defaultdict\nfrom collections.abc import Awaitable, Sequence\nfrom contextlib import suppress\nfrom importlib.resources import as_file, files\nfrom threading import Event, Thread\nfrom typing import Any, Callable\n\nimport websockets\nimport websockets.exceptions\nimport websockets.server\n\nfrom qcodes.parameters import Parameter\n\nWEBSOCKET_PORT = 5678\nSERVER_PORT = 3000\n\nlog = logging.getLogger(__name__)\n\n\ndef _get_metadata(\n    *parameters: Parameter, use_root_instrument: bool = True\n) -> dict[str, Any]:\n    \"\"\"\n    Return a dictionary that contains the parameter metadata grouped by the\n    instrument it belongs to.\n    \"\"\"\n    metadata_timestamp = time.time()\n    # group metadata by instrument\n    metas: dict[Any, Any] = defaultdict(list)\n    for parameter in parameters:\n        # Get the latest value from the parameter,\n        # respecting the max_val_age parameter\n        meta: dict[str, float | str | None] = {}\n        meta[\"value\"] = str(parameter.get_latest())\n        timestamp = parameter.get_latest.get_timestamp()\n        if timestamp is not None:\n            meta[\"ts\"] = timestamp.timestamp()\n        else:\n            meta[\"ts\"] = None\n        meta[\"name\"] = parameter.label or parameter.name\n        meta[\"unit\"] = parameter.unit\n\n        # find the base instrument that this parameter belongs to\n        if use_root_instrument:\n            baseinst = parameter.root_instrument\n        else:\n            baseinst = parameter.instrument\n        if baseinst is None:\n            metas[\"Unbound Parameter\"].append(meta)\n        else:\n            metas[str(baseinst)].append(meta)\n\n    # Create list of parameters, grouped by instrument\n    parameters_out = []\n    for instrument in metas:\n        temp = {\"instrument\": instrument, \"parameters\": metas[instrument]}\n        parameters_out.append(temp)\n\n    state = {\"ts\": metadata_timestamp, \"parameters\": parameters_out}\n    return state\n\n\ndef _handler(\n    parameters: Sequence[Parameter], interval: float, use_root_instrument: bool = True\n) -> Callable[[websockets.server.WebSocketServerProtocol, str], Awaitable[None]]:\n    \"\"\"\n    Return the websockets server handler.\n    \"\"\"\n\n    async def server_func(\n        websocket: websockets.server.WebSocketServerProtocol, _: str\n    ) -> None:\n        \"\"\"\n        Create a websockets handler that sends parameter values to a listener\n        every \"interval\" seconds.\n        \"\"\"\n        while True:\n            try:\n                # Update the parameter values\n                try:\n                    meta = _get_metadata(\n                        *parameters, use_root_instrument=use_root_instrument\n                    )\n                except ValueError:\n                    log.exception(\"Error getting parameters\")\n                    break\n                log.debug(\"sending.. to %r\", websocket)\n                await websocket.send(json.dumps(meta))\n                # Wait for interval seconds and then send again\n                await asyncio.sleep(interval)\n            except (CancelledError, websockets.exceptions.ConnectionClosed):\n                log.debug(\"Got CancelledError or ConnectionClosed\",\n                          exc_info=True)\n                break\n        log.debug(\"Closing websockets connection\")\n\n    return server_func\n\n\nclass Monitor(Thread):\n    \"\"\"\n    QCodes Monitor - WebSockets server to monitor qcodes parameters.\n    \"\"\"\n    running = None\n\n    def __init__(\n        self,\n        *parameters: Parameter,\n        interval: float = 1,\n        use_root_instrument: bool = True,\n    ):\n        \"\"\"\n        Monitor qcodes parameters.\n\n        Args:\n            *parameters: Parameters to monitor.\n            interval: How often one wants to refresh the values.\n            use_root_instrument: Defines if parameters are grouped according to\n                                parameter.root_instrument or parameter.instrument\n        \"\"\"\n        super().__init__()\n\n        # Check that all values are valid parameters\n        for parameter in parameters:\n            if not isinstance(parameter, Parameter):\n                raise TypeError(f\"We can only monitor QCodes \"\n                                f\"Parameters, not {type(parameter)}\")\n\n        self.loop: asyncio.AbstractEventLoop | None = None\n        self._stop_loop_future: asyncio.Future | None = None\n        self._parameters = parameters\n        self.loop_is_closed = Event()\n        self.server_is_started = Event()\n        self.handler = _handler(\n            parameters, interval=interval, use_root_instrument=use_root_instrument\n        )\n        log.debug(\"Start monitoring thread\")\n        if Monitor.running:\n            # stop the old server\n            log.debug(\"Stopping and restarting server\")\n            Monitor.running.stop()\n        self.start()\n\n        # Wait until the loop is running\n        self.server_is_started.wait(timeout=5)\n        if not self.server_is_started.is_set():\n            raise RuntimeError(\"Failed to start server\")\n        Monitor.running = self\n\n    def run(self) -> None:\n        \"\"\"\n        Start the event loop and run forever.\n        \"\"\"\n        log.debug(\"Running Websocket server\")\n\n        async def run_loop() -> None:\n            self.loop = asyncio.get_running_loop()\n            self._stop_loop_future = self.loop.create_future()\n\n            async with websockets.server.serve(\n                self.handler, \"127.0.0.1\", WEBSOCKET_PORT, close_timeout=1\n            ):\n                self.server_is_started.set()\n                try:\n                    await self._stop_loop_future\n                except asyncio.CancelledError:\n                    log.debug(\"Websocket server thread shutting down\")\n\n        try:\n            asyncio.run(run_loop())\n        finally:\n            self.loop_is_closed.set()\n\n    def update_all(self) -> None:\n        \"\"\"\n        Update all parameters in the monitor.\n        \"\"\"\n        for parameter in self._parameters:\n            # call get if it can be called without arguments\n            with suppress(TypeError):\n                parameter.get()\n\n    def stop(self) -> None:\n        \"\"\"\n        Shutdown the server, close the event loop and join the thread.\n        Setting active Monitor to ``None``.\n        \"\"\"\n        self.join()\n        Monitor.running = None\n\n    def join(self, timeout: float | None = None) -> None:\n        \"\"\"\n        Overwrite ``Thread.join`` to make sure server is stopped before\n        joining avoiding a potential deadlock.\n        \"\"\"\n        log.debug(\"Shutting down server\")\n        if not self.is_alive():\n            # we run this check before trying to run to prevent a cryptic\n            # error message\n            log.debug(\"monitor is dead\")\n            return\n        try:\n            if self.loop is not None and self._stop_loop_future is not None:\n                log.debug(\"Instructing server to stop event loop.\")\n                self.loop.call_soon_threadsafe(self._stop_loop_future.cancel)\n            else:\n                log.debug(\"No event loop found. Cannot stop event loop.\")\n        except RuntimeError:\n            # the above may throw a runtime error if the loop is already\n            # stopped in which case there is nothing more to do\n            log.exception(\"Could not close loop\")\n        self.loop_is_closed.wait(timeout=5)\n        if not self.loop_is_closed.is_set():\n            raise RuntimeError(\"Failed to join loop\")\n        log.debug(\"Loop reported closed\")\n        super().join(timeout=timeout)\n        log.debug(\"Monitor Thread has joined\")\n\n    @staticmethod\n    def show() -> None:\n        \"\"\"\n        Overwrite this method to show/raise your monitor GUI\n        F.ex.\n\n        ::\n\n            import webbrowser\n            url = \"localhost:3000\"\n            # Open URL in new window, raising the window if possible.\n            webbrowser.open_new(url)\n\n        \"\"\"\n        webbrowser.open(f\"http://localhost:{SERVER_PORT}\")\n\n\ndef main() -> None:\n    import http.server\n\n    # If this file is run, create a simple webserver that serves a simple\n    # website that can be used to view monitored parameters.\n    # # https://github.com/python/mypy/issues/4182\n    parent_module = \".\".join(__loader__.name.split(\".\")[:-1])  # type: ignore[name-defined]\n\n    static_dir = files(parent_module).joinpath(\"dist\")\n    try:\n        with as_file(static_dir) as extracted_dir:\n            os.chdir(extracted_dir)\n            log.info(\"Starting HTTP Server at http://localhost:%i\", SERVER_PORT)\n            with socketserver.TCPServer(\n                (\"\", SERVER_PORT), http.server.SimpleHTTPRequestHandler\n            ) as httpd:\n                log.debug(\"serving directory %s\", static_dir)\n                webbrowser.open(f\"http://localhost:{SERVER_PORT}\")\n                httpd.serve_forever()\n    except KeyboardInterrupt:\n        log.info(\"Shutting Down HTTP Server\")\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "src/qcodes/monitor/monitor.py"}], "after_files": [{"content": "#! /usr/bin/env python\n# vim:fenc=utf-8\n#\n# Copyright \u00a9 2017 unga <giulioungaretti@me.com>\n#\n# Distributed under terms of the MIT license.\n\"\"\"\nMonitor a set of parameters in a background thread\nstream output over websocket\n\nTo start monitor, run this file, or if qcodes is installed as a module:\n\n``% python -m qcodes.monitor.monitor``\n\nAdd parameters to monitor in your measurement by creating a new monitor with a\nlist of parameters to monitor:\n\n``monitor = qcodes.Monitor(param1, param2, param3, ...)``\n\"\"\"\nfrom __future__ import annotations\n\nimport asyncio\nimport json\nimport logging\nimport os\nimport socketserver\nimport time\nimport webbrowser\nfrom asyncio import CancelledError\nfrom collections import defaultdict\nfrom collections.abc import Awaitable, Sequence\nfrom contextlib import suppress\nfrom importlib.resources import as_file, files\nfrom threading import Event, Thread\nfrom typing import Any, Callable\n\nimport websockets\nimport websockets.exceptions\nimport websockets.server\n\nfrom qcodes.parameters import Parameter\n\nWEBSOCKET_PORT = 5678\nSERVER_PORT = 3000\n\nlog = logging.getLogger(__name__)\n\n\ndef _get_metadata(\n    *parameters: Parameter, use_root_instrument: bool = True\n) -> dict[str, Any]:\n    \"\"\"\n    Return a dictionary that contains the parameter metadata grouped by the\n    instrument it belongs to.\n    \"\"\"\n    metadata_timestamp = time.time()\n    # group metadata by instrument\n    metas: dict[Any, Any] = defaultdict(list)\n    for parameter in parameters:\n        # Get the latest value from the parameter,\n        # respecting the max_val_age parameter\n        meta: dict[str, float | str | None] = {}\n        meta[\"value\"] = str(parameter.get_latest())\n        timestamp = parameter.get_latest.get_timestamp()\n        if timestamp is not None:\n            meta[\"ts\"] = timestamp.timestamp()\n        else:\n            meta[\"ts\"] = None\n        meta[\"name\"] = parameter.label or parameter.name\n        meta[\"unit\"] = parameter.unit\n\n        # find the base instrument that this parameter belongs to\n        if use_root_instrument:\n            baseinst = parameter.root_instrument\n        else:\n            baseinst = parameter.instrument\n        if baseinst is None:\n            metas[\"Unbound Parameter\"].append(meta)\n        else:\n            metas[str(baseinst)].append(meta)\n\n    # Create list of parameters, grouped by instrument\n    parameters_out = []\n    for instrument in metas:\n        temp = {\"instrument\": instrument, \"parameters\": metas[instrument]}\n        parameters_out.append(temp)\n\n    state = {\"ts\": metadata_timestamp, \"parameters\": parameters_out}\n    return state\n\n\ndef _handler(\n    parameters: Sequence[Parameter], interval: float, use_root_instrument: bool = True\n) -> Callable[[websockets.server.WebSocketServerProtocol, str], Awaitable[None]]:\n    \"\"\"\n    Return the websockets server handler.\n    \"\"\"\n\n    async def server_func(\n        websocket: websockets.server.WebSocketServerProtocol, _: str\n    ) -> None:\n        \"\"\"\n        Create a websockets handler that sends parameter values to a listener\n        every \"interval\" seconds.\n        \"\"\"\n        while True:\n            try:\n                # Update the parameter values\n                try:\n                    meta = _get_metadata(\n                        *parameters, use_root_instrument=use_root_instrument\n                    )\n                except ValueError:\n                    log.exception(\"Error getting parameters\")\n                    break\n                log.debug(\"sending.. to %r\", websocket)\n                await websocket.send(json.dumps(meta))\n                # Wait for interval seconds and then send again\n                await asyncio.sleep(interval)\n            except (CancelledError, websockets.exceptions.ConnectionClosed):\n                log.debug(\"Got CancelledError or ConnectionClosed\",\n                          exc_info=True)\n                break\n        log.debug(\"Closing websockets connection\")\n\n    return server_func\n\n\nclass Monitor(Thread):\n    \"\"\"\n    QCodes Monitor - WebSockets server to monitor qcodes parameters.\n    \"\"\"\n    running = None\n\n    def __init__(\n        self,\n        *parameters: Parameter,\n        interval: float = 1,\n        use_root_instrument: bool = True,\n    ):\n        \"\"\"\n        Monitor qcodes parameters.\n\n        Args:\n            *parameters: Parameters to monitor.\n            interval: How often one wants to refresh the values.\n            use_root_instrument: Defines if parameters are grouped according to\n                                parameter.root_instrument or parameter.instrument\n        \"\"\"\n        super().__init__(daemon=True)\n\n        # Check that all values are valid parameters\n        for parameter in parameters:\n            if not isinstance(parameter, Parameter):\n                raise TypeError(f\"We can only monitor QCodes \"\n                                f\"Parameters, not {type(parameter)}\")\n\n        self.loop: asyncio.AbstractEventLoop | None = None\n        self._stop_loop_future: asyncio.Future | None = None\n        self._parameters = parameters\n        self.loop_is_closed = Event()\n        self.server_is_started = Event()\n        self.handler = _handler(\n            parameters, interval=interval, use_root_instrument=use_root_instrument\n        )\n        log.debug(\"Start monitoring thread\")\n        if Monitor.running:\n            # stop the old server\n            log.debug(\"Stopping and restarting server\")\n            Monitor.running.stop()\n        self.start()\n\n        # Wait until the loop is running\n        self.server_is_started.wait(timeout=5)\n        if not self.server_is_started.is_set():\n            raise RuntimeError(\"Failed to start server\")\n        Monitor.running = self\n\n    def run(self) -> None:\n        \"\"\"\n        Start the event loop and run forever.\n        \"\"\"\n        log.debug(\"Running Websocket server\")\n\n        async def run_loop() -> None:\n            self.loop = asyncio.get_running_loop()\n            self._stop_loop_future = self.loop.create_future()\n\n            async with websockets.server.serve(\n                self.handler, \"127.0.0.1\", WEBSOCKET_PORT, close_timeout=1\n            ):\n                self.server_is_started.set()\n                try:\n                    await self._stop_loop_future\n                except asyncio.CancelledError:\n                    log.debug(\"Websocket server thread shutting down\")\n\n        try:\n            asyncio.run(run_loop())\n        finally:\n            self.loop_is_closed.set()\n\n    def update_all(self) -> None:\n        \"\"\"\n        Update all parameters in the monitor.\n        \"\"\"\n        for parameter in self._parameters:\n            # call get if it can be called without arguments\n            with suppress(TypeError):\n                parameter.get()\n\n    def stop(self) -> None:\n        \"\"\"\n        Shutdown the server, close the event loop and join the thread.\n        Setting active Monitor to ``None``.\n        \"\"\"\n        self.join()\n        Monitor.running = None\n\n    def join(self, timeout: float | None = None) -> None:\n        \"\"\"\n        Overwrite ``Thread.join`` to make sure server is stopped before\n        joining avoiding a potential deadlock.\n        \"\"\"\n        log.debug(\"Shutting down server\")\n        if not self.is_alive():\n            # we run this check before trying to run to prevent a cryptic\n            # error message\n            log.debug(\"monitor is dead\")\n            return\n        try:\n            if self.loop is not None and self._stop_loop_future is not None:\n                log.debug(\"Instructing server to stop event loop.\")\n                self.loop.call_soon_threadsafe(self._stop_loop_future.cancel)\n            else:\n                log.debug(\"No event loop found. Cannot stop event loop.\")\n        except RuntimeError:\n            # the above may throw a runtime error if the loop is already\n            # stopped in which case there is nothing more to do\n            log.exception(\"Could not close loop\")\n        self.loop_is_closed.wait(timeout=5)\n        if not self.loop_is_closed.is_set():\n            raise RuntimeError(\"Failed to join loop\")\n        log.debug(\"Loop reported closed\")\n        super().join(timeout=timeout)\n        log.debug(\"Monitor Thread has joined\")\n\n    @staticmethod\n    def show() -> None:\n        \"\"\"\n        Overwrite this method to show/raise your monitor GUI\n        F.ex.\n\n        ::\n\n            import webbrowser\n            url = \"localhost:3000\"\n            # Open URL in new window, raising the window if possible.\n            webbrowser.open_new(url)\n\n        \"\"\"\n        webbrowser.open(f\"http://localhost:{SERVER_PORT}\")\n\n\ndef main() -> None:\n    import http.server\n\n    # If this file is run, create a simple webserver that serves a simple\n    # website that can be used to view monitored parameters.\n    # # https://github.com/python/mypy/issues/4182\n    parent_module = \".\".join(__loader__.name.split(\".\")[:-1])  # type: ignore[name-defined]\n\n    static_dir = files(parent_module).joinpath(\"dist\")\n    try:\n        with as_file(static_dir) as extracted_dir:\n            os.chdir(extracted_dir)\n            log.info(\"Starting HTTP Server at http://localhost:%i\", SERVER_PORT)\n            with socketserver.TCPServer(\n                (\"\", SERVER_PORT), http.server.SimpleHTTPRequestHandler\n            ) as httpd:\n                log.debug(\"serving directory %s\", static_dir)\n                webbrowser.open(f\"http://localhost:{SERVER_PORT}\")\n                httpd.serve_forever()\n    except KeyboardInterrupt:\n        log.info(\"Shutting Down HTTP Server\")\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "src/qcodes/monitor/monitor.py"}]}