We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
REPL client help is missing some commands
The help for the REPL client is missing some commands from `help`, and instead has `client.{c[0]}`.

```
pymodbus.console tcp
❯ pymodbus.console tcp

----------------------------------------------------------------------------
__________          _____             .___  __________              .__
\______   \___.__. /     \   ____   __| _/  \______   \ ____ ______ |  |
 |     ___<   |  |/  \ /  \ /  _ \ / __ |    |       _// __ \\\____ \|  |
 |    |    \___  /    Y    (  <_> ) /_/ |    |    |   \  ___/|  |_> >  |__
 |____|    / ____\____|__  /\____/\____ | /\ |____|_  /\___  >   __/|____/
           \/            \/            \/ \/        \/     \/|__|
                                        v1.3.0 - 3.5.0dev
----------------------------------------------------------------------------

> help
Available commands:
<...>
client.{c[0]}
```

This was caught by a `ruff` rule.
> RUF011 Dictionary comprehension uses static key: `"client.{c[0]}"`


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pymodbus/repl/client/helper.py`
Content:
```
1 """Helper Module for REPL actions."""
2 import inspect
3 
4 # pylint: disable=missing-type-doc
5 import json
6 from collections import OrderedDict
7 from typing import Any, Dict, List, Union
8 
9 import pygments
10 from prompt_toolkit import print_formatted_text
11 from prompt_toolkit.formatted_text import HTML, PygmentsTokens
12 from pygments.lexers.data import JsonLexer
13 
14 from pymodbus.payload import BinaryPayloadDecoder, Endian
15 
16 
17 predicate = inspect.isfunction
18 argspec = inspect.signature
19 
20 
21 FORMATTERS = {
22     "int8": "decode_8bit_int",
23     "int16": "decode_16bit_int",
24     "int32": "decode_32bit_int",
25     "int64": "decode_64bit_int",
26     "uint8": "decode_8bit_uint",
27     "uint16": "decode_16bit_uint",
28     "uint32": "decode_32bit_uint",
29     "uint64": "decode_64bit_int",
30     "float16": "decode_16bit_float",
31     "float32": "decode_32bit_float",
32     "float64": "decode_64bit_float",
33 }
34 
35 
36 DEFAULT_KWARGS = {"slave": "Slave address"}
37 
38 OTHER_COMMANDS = {
39     "result.raw": "Show RAW Result",
40     "result.decode": "Decode register response to known formats",
41 }
42 EXCLUDE = ["execute", "recv", "send", "trace", "set_debug"]
43 CLIENT_METHODS = [
44     "connect",
45     "close",
46     "idle_time",
47     "is_socket_open",
48     "get_port",
49     "set_port",
50     "get_stopbits",
51     "set_stopbits",
52     "get_bytesize",
53     "set_bytesize",
54     "get_parity",
55     "set_parity",
56     "get_baudrate",
57     "set_baudrate",
58     "get_timeout",
59     "set_timeout",
60     "get_serial_settings",
61 ]
62 CLIENT_ATTRIBUTES: List[str] = []
63 
64 
65 class Command:
66     """Class representing Commands to be consumed by Completer."""
67 
68     def __init__(self, name, signature, doc, slave=False):
69         """Initialize.
70 
71         :param name: Name of the command
72         :param signature: inspect object
73         :param doc: Doc string for the command
74         :param slave: Use slave as additional argument in the command .
75         """
76         self.name = name
77         self.doc = doc.split("\n") if doc else " ".join(name.split("_"))
78         self.help_text = self._create_help()
79         self.param_help = self._create_arg_help()
80         if signature:
81             self._params = signature.parameters
82             self.args = self.create_completion()
83         else:
84             self._params = ""
85 
86         if self.name.startswith("client.") and slave:
87             self.args.update(**DEFAULT_KWARGS)
88 
89     def _create_help(self):
90         """Create help."""
91         doc = filter(lambda d: d, self.doc)
92         cmd_help = list(
93             filter(
94                 lambda x: not x.startswith(":param") and not x.startswith(":return"),
95                 doc,
96             )
97         )
98         return " ".join(cmd_help).strip()
99 
100     def _create_arg_help(self):
101         """Create arg help."""
102         param_dict = {}
103         params = list(filter(lambda d: d.strip().startswith(":param"), self.doc))
104         for param in params:
105             param, param_help = param.split(":param")[1].strip().split(":")
106             param_dict[param] = param_help
107         return param_dict
108 
109     def create_completion(self):
110         """Create command completion meta data.
111 
112         :return:
113         """
114         words = {}
115 
116         def _create(entry, default):
117             if entry not in ["self", "kwargs"]:
118                 if isinstance(default, (int, str)):
119                     entry += f"={default}"
120                 return entry
121             return None
122 
123         for arg in self._params.values():
124             if entry := _create(arg.name, arg.default):
125                 entry, meta = self.get_meta(entry)
126                 words[entry] = meta
127 
128         return words
129 
130     def get_completion(self):
131         """Get a list of completions.
132 
133         :return:
134         """
135         return self.args.keys()
136 
137     def get_meta(self, cmd):
138         """Get Meta info of a given command.
139 
140         :param cmd: Name of command.
141         :return: Dict containing meta info.
142         """
143         cmd = cmd.strip()
144         cmd = cmd.split("=")[0].strip()
145         return cmd, self.param_help.get(cmd, "")
146 
147     def __str__(self):
148         """Return string representation."""
149         if self.doc:
150             return "Command {:>50}{:<20}".format(  # pylint: disable=consider-using-f-string
151                 self.name, self.doc
152             )
153         return f"Command {self.name}"
154 
155 
156 def _get_requests(members):
157     """Get requests."""
158     commands = list(
159         filter(
160             lambda x: (
161                 x[0] not in EXCLUDE and x[0] not in CLIENT_METHODS and callable(x[1])
162             ),
163             members,
164         )
165     )
166     commands = {
167         f"client.{c[0]}": Command(
168             f"client.{c[0]}", argspec(c[1]), inspect.getdoc(c[1]), slave=False
169         )
170         for c in commands
171         if not c[0].startswith("_")
172     }
173     return commands
174 
175 
176 def _get_client_methods(members):
177     """Get client methods."""
178     commands = list(
179         filter(lambda x: (x[0] not in EXCLUDE and x[0] in CLIENT_METHODS), members)
180     )
181     commands = {
182         "client.{c[0]}": Command(
183             "client.{c[0]}", argspec(c[1]), inspect.getdoc(c[1]), slave=False
184         )
185         for c in commands
186         if not c[0].startswith("_")
187     }
188     return commands
189 
190 
191 def _get_client_properties(members):
192     """Get client properties."""
193     global CLIENT_ATTRIBUTES  # pylint: disable=global-variable-not-assigned
194     commands = list(filter(lambda x: not callable(x[1]), members))
195     commands = {
196         f"client.{c[0]}": Command(f"client.{c[0]}", None, "Read Only!", slave=False)
197         for c in commands
198         if (not c[0].startswith("_") and isinstance(c[1], (str, int, float)))
199     }
200     CLIENT_ATTRIBUTES.extend(list(commands.keys()))
201     return commands
202 
203 
204 def get_commands(client):
205     """Retrieve all required methods and attributes.
206 
207     Of a client object and convert it to commands.
208 
209     :param client: Modbus Client object.
210     :return:
211     """
212     commands = {}
213     members = inspect.getmembers(client)
214     requests = _get_requests(members)
215     client_methods = _get_client_methods(members)
216     client_attr = _get_client_properties(members)
217 
218     result_commands = inspect.getmembers(Result, predicate=predicate)
219     result_commands = {
220         f"result.{c[0]}": Command(f"result.{c[0]}", argspec(c[1]), inspect.getdoc(c[1]))
221         for c in result_commands
222         if (not c[0].startswith("_") and c[0] != "print_result")
223     }
224     commands.update(requests)
225     commands.update(client_methods)
226     commands.update(client_attr)
227     commands.update(result_commands)
228     return commands
229 
230 
231 class Result:
232     """Represent result command."""
233 
234     function_code: int = None
235     data: Union[Dict[int, Any], Any] = None
236 
237     def __init__(self, result):
238         """Initialize.
239 
240         :param result: Response of a modbus command.
241         """
242         if isinstance(result, dict):  # Modbus response
243             self.function_code = result.pop("function_code", None)
244             self.data = dict(result)
245         else:
246             self.data = result
247 
248     def decode(self, formatters, byte_order="big", word_order="big"):
249         """Decode the register response to known formatters.
250 
251         :param formatters: int8/16/32/64, uint8/16/32/64, float32/64
252         :param byte_order: little/big
253         :param word_order: little/big
254         """
255         # Read Holding Registers (3)
256         # Read Input Registers (4)
257         # Read Write Registers (23)
258         if not isinstance(formatters, (list, tuple)):
259             formatters = [formatters]
260 
261         if self.function_code not in [3, 4, 23]:
262             print_formatted_text(HTML("<red>Decoder works only for registers!!</red>"))
263             return
264         byte_order = (
265             Endian.Little if byte_order.strip().lower() == "little" else Endian.Big
266         )
267         word_order = (
268             Endian.Little if word_order.strip().lower() == "little" else Endian.Big
269         )
270         decoder = BinaryPayloadDecoder.fromRegisters(
271             self.data.get("registers"), byteorder=byte_order, wordorder=word_order
272         )
273         for formatter in formatters:
274             if not (formatter := FORMATTERS.get(formatter)):
275                 print_formatted_text(
276                     HTML(f"<red>Invalid Formatter - {formatter}!!</red>")
277                 )
278                 return
279             decoded = getattr(decoder, formatter)()
280             self.print_result(decoded)
281 
282     def raw(self):
283         """Return raw result dict."""
284         self.print_result()
285 
286     def _process_dict(self, use_dict):
287         """Process dict."""
288         new_dict = OrderedDict()
289         for k, v_item in use_dict.items():
290             if isinstance(v_item, bytes):
291                 v_item = v_item.decode("utf-8")
292             elif isinstance(v_item, dict):
293                 v_item = self._process_dict(v_item)
294             elif isinstance(v_item, (list, tuple)):
295                 v_item = [
296                     v1.decode("utf-8") if isinstance(v1, bytes) else v1 for v1 in v_item
297                 ]
298             new_dict[k] = v_item
299         return new_dict
300 
301     def print_result(self, data=None):
302         """Print result object pretty.
303 
304         :param data: Data to be printed.
305         """
306         data = data or self.data
307         if isinstance(data, dict):
308             data = self._process_dict(data)
309         elif isinstance(data, (list, tuple)):
310             data = [v.decode("utf-8") if isinstance(v, bytes) else v for v in data]
311         elif isinstance(data, bytes):
312             data = data.decode("utf-8")
313         tokens = list(pygments.lex(json.dumps(data, indent=4), lexer=JsonLexer()))
314         print_formatted_text(PygmentsTokens(tokens))
315
```
--- 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/pymodbus/repl/client/helper.py b/pymodbus/repl/client/helper.py
--- a/pymodbus/repl/client/helper.py
+++ b/pymodbus/repl/client/helper.py
@@ -179,8 +179,8 @@
         filter(lambda x: (x[0] not in EXCLUDE and x[0] in CLIENT_METHODS), members)
     )
     commands = {
-        "client.{c[0]}": Command(
-            "client.{c[0]}", argspec(c[1]), inspect.getdoc(c[1]), slave=False
+        f"client.{c[0]}": Command(
+            f"client.{c[0]}", argspec(c[1]), inspect.getdoc(c[1]), slave=False
         )
         for c in commands
         if not c[0].startswith("_")

{"golden_diff": "diff --git a/pymodbus/repl/client/helper.py b/pymodbus/repl/client/helper.py\n--- a/pymodbus/repl/client/helper.py\n+++ b/pymodbus/repl/client/helper.py\n@@ -179,8 +179,8 @@\n         filter(lambda x: (x[0] not in EXCLUDE and x[0] in CLIENT_METHODS), members)\n     )\n     commands = {\n-        \"client.{c[0]}\": Command(\n-            \"client.{c[0]}\", argspec(c[1]), inspect.getdoc(c[1]), slave=False\n+        f\"client.{c[0]}\": Command(\n+            f\"client.{c[0]}\", argspec(c[1]), inspect.getdoc(c[1]), slave=False\n         )\n         for c in commands\n         if not c[0].startswith(\"_\")\n", "issue": "REPL client help is missing some commands\nThe help for the REPL client is missing some commands from `help`, and instead has `client.{c[0]}`.\r\n\r\n```\r\npymodbus.console tcp\r\n\u276f pymodbus.console tcp\r\n\r\n----------------------------------------------------------------------------\r\n__________          _____             .___  __________              .__\r\n\\______   \\___.__. /     \\   ____   __| _/  \\______   \\ ____ ______ |  |\r\n |     ___<   |  |/  \\ /  \\ /  _ \\ / __ |    |       _// __ \\\\\\____ \\|  |\r\n |    |    \\___  /    Y    (  <_> ) /_/ |    |    |   \\  ___/|  |_> >  |__\r\n |____|    / ____\\____|__  /\\____/\\____ | /\\ |____|_  /\\___  >   __/|____/\r\n           \\/            \\/            \\/ \\/        \\/     \\/|__|\r\n                                        v1.3.0 - 3.5.0dev\r\n----------------------------------------------------------------------------\r\n\r\n> help\r\nAvailable commands:\r\n<...>\r\nclient.{c[0]}\r\n```\r\n\r\nThis was caught by a `ruff` rule.\r\n> RUF011 Dictionary comprehension uses static key: `\"client.{c[0]}\"`\r\n\n", "before_files": [{"content": "\"\"\"Helper Module for REPL actions.\"\"\"\nimport inspect\n\n# pylint: disable=missing-type-doc\nimport json\nfrom collections import OrderedDict\nfrom typing import Any, Dict, List, Union\n\nimport pygments\nfrom prompt_toolkit import print_formatted_text\nfrom prompt_toolkit.formatted_text import HTML, PygmentsTokens\nfrom pygments.lexers.data import JsonLexer\n\nfrom pymodbus.payload import BinaryPayloadDecoder, Endian\n\n\npredicate = inspect.isfunction\nargspec = inspect.signature\n\n\nFORMATTERS = {\n    \"int8\": \"decode_8bit_int\",\n    \"int16\": \"decode_16bit_int\",\n    \"int32\": \"decode_32bit_int\",\n    \"int64\": \"decode_64bit_int\",\n    \"uint8\": \"decode_8bit_uint\",\n    \"uint16\": \"decode_16bit_uint\",\n    \"uint32\": \"decode_32bit_uint\",\n    \"uint64\": \"decode_64bit_int\",\n    \"float16\": \"decode_16bit_float\",\n    \"float32\": \"decode_32bit_float\",\n    \"float64\": \"decode_64bit_float\",\n}\n\n\nDEFAULT_KWARGS = {\"slave\": \"Slave address\"}\n\nOTHER_COMMANDS = {\n    \"result.raw\": \"Show RAW Result\",\n    \"result.decode\": \"Decode register response to known formats\",\n}\nEXCLUDE = [\"execute\", \"recv\", \"send\", \"trace\", \"set_debug\"]\nCLIENT_METHODS = [\n    \"connect\",\n    \"close\",\n    \"idle_time\",\n    \"is_socket_open\",\n    \"get_port\",\n    \"set_port\",\n    \"get_stopbits\",\n    \"set_stopbits\",\n    \"get_bytesize\",\n    \"set_bytesize\",\n    \"get_parity\",\n    \"set_parity\",\n    \"get_baudrate\",\n    \"set_baudrate\",\n    \"get_timeout\",\n    \"set_timeout\",\n    \"get_serial_settings\",\n]\nCLIENT_ATTRIBUTES: List[str] = []\n\n\nclass Command:\n    \"\"\"Class representing Commands to be consumed by Completer.\"\"\"\n\n    def __init__(self, name, signature, doc, slave=False):\n        \"\"\"Initialize.\n\n        :param name: Name of the command\n        :param signature: inspect object\n        :param doc: Doc string for the command\n        :param slave: Use slave as additional argument in the command .\n        \"\"\"\n        self.name = name\n        self.doc = doc.split(\"\\n\") if doc else \" \".join(name.split(\"_\"))\n        self.help_text = self._create_help()\n        self.param_help = self._create_arg_help()\n        if signature:\n            self._params = signature.parameters\n            self.args = self.create_completion()\n        else:\n            self._params = \"\"\n\n        if self.name.startswith(\"client.\") and slave:\n            self.args.update(**DEFAULT_KWARGS)\n\n    def _create_help(self):\n        \"\"\"Create help.\"\"\"\n        doc = filter(lambda d: d, self.doc)\n        cmd_help = list(\n            filter(\n                lambda x: not x.startswith(\":param\") and not x.startswith(\":return\"),\n                doc,\n            )\n        )\n        return \" \".join(cmd_help).strip()\n\n    def _create_arg_help(self):\n        \"\"\"Create arg help.\"\"\"\n        param_dict = {}\n        params = list(filter(lambda d: d.strip().startswith(\":param\"), self.doc))\n        for param in params:\n            param, param_help = param.split(\":param\")[1].strip().split(\":\")\n            param_dict[param] = param_help\n        return param_dict\n\n    def create_completion(self):\n        \"\"\"Create command completion meta data.\n\n        :return:\n        \"\"\"\n        words = {}\n\n        def _create(entry, default):\n            if entry not in [\"self\", \"kwargs\"]:\n                if isinstance(default, (int, str)):\n                    entry += f\"={default}\"\n                return entry\n            return None\n\n        for arg in self._params.values():\n            if entry := _create(arg.name, arg.default):\n                entry, meta = self.get_meta(entry)\n                words[entry] = meta\n\n        return words\n\n    def get_completion(self):\n        \"\"\"Get a list of completions.\n\n        :return:\n        \"\"\"\n        return self.args.keys()\n\n    def get_meta(self, cmd):\n        \"\"\"Get Meta info of a given command.\n\n        :param cmd: Name of command.\n        :return: Dict containing meta info.\n        \"\"\"\n        cmd = cmd.strip()\n        cmd = cmd.split(\"=\")[0].strip()\n        return cmd, self.param_help.get(cmd, \"\")\n\n    def __str__(self):\n        \"\"\"Return string representation.\"\"\"\n        if self.doc:\n            return \"Command {:>50}{:<20}\".format(  # pylint: disable=consider-using-f-string\n                self.name, self.doc\n            )\n        return f\"Command {self.name}\"\n\n\ndef _get_requests(members):\n    \"\"\"Get requests.\"\"\"\n    commands = list(\n        filter(\n            lambda x: (\n                x[0] not in EXCLUDE and x[0] not in CLIENT_METHODS and callable(x[1])\n            ),\n            members,\n        )\n    )\n    commands = {\n        f\"client.{c[0]}\": Command(\n            f\"client.{c[0]}\", argspec(c[1]), inspect.getdoc(c[1]), slave=False\n        )\n        for c in commands\n        if not c[0].startswith(\"_\")\n    }\n    return commands\n\n\ndef _get_client_methods(members):\n    \"\"\"Get client methods.\"\"\"\n    commands = list(\n        filter(lambda x: (x[0] not in EXCLUDE and x[0] in CLIENT_METHODS), members)\n    )\n    commands = {\n        \"client.{c[0]}\": Command(\n            \"client.{c[0]}\", argspec(c[1]), inspect.getdoc(c[1]), slave=False\n        )\n        for c in commands\n        if not c[0].startswith(\"_\")\n    }\n    return commands\n\n\ndef _get_client_properties(members):\n    \"\"\"Get client properties.\"\"\"\n    global CLIENT_ATTRIBUTES  # pylint: disable=global-variable-not-assigned\n    commands = list(filter(lambda x: not callable(x[1]), members))\n    commands = {\n        f\"client.{c[0]}\": Command(f\"client.{c[0]}\", None, \"Read Only!\", slave=False)\n        for c in commands\n        if (not c[0].startswith(\"_\") and isinstance(c[1], (str, int, float)))\n    }\n    CLIENT_ATTRIBUTES.extend(list(commands.keys()))\n    return commands\n\n\ndef get_commands(client):\n    \"\"\"Retrieve all required methods and attributes.\n\n    Of a client object and convert it to commands.\n\n    :param client: Modbus Client object.\n    :return:\n    \"\"\"\n    commands = {}\n    members = inspect.getmembers(client)\n    requests = _get_requests(members)\n    client_methods = _get_client_methods(members)\n    client_attr = _get_client_properties(members)\n\n    result_commands = inspect.getmembers(Result, predicate=predicate)\n    result_commands = {\n        f\"result.{c[0]}\": Command(f\"result.{c[0]}\", argspec(c[1]), inspect.getdoc(c[1]))\n        for c in result_commands\n        if (not c[0].startswith(\"_\") and c[0] != \"print_result\")\n    }\n    commands.update(requests)\n    commands.update(client_methods)\n    commands.update(client_attr)\n    commands.update(result_commands)\n    return commands\n\n\nclass Result:\n    \"\"\"Represent result command.\"\"\"\n\n    function_code: int = None\n    data: Union[Dict[int, Any], Any] = None\n\n    def __init__(self, result):\n        \"\"\"Initialize.\n\n        :param result: Response of a modbus command.\n        \"\"\"\n        if isinstance(result, dict):  # Modbus response\n            self.function_code = result.pop(\"function_code\", None)\n            self.data = dict(result)\n        else:\n            self.data = result\n\n    def decode(self, formatters, byte_order=\"big\", word_order=\"big\"):\n        \"\"\"Decode the register response to known formatters.\n\n        :param formatters: int8/16/32/64, uint8/16/32/64, float32/64\n        :param byte_order: little/big\n        :param word_order: little/big\n        \"\"\"\n        # Read Holding Registers (3)\n        # Read Input Registers (4)\n        # Read Write Registers (23)\n        if not isinstance(formatters, (list, tuple)):\n            formatters = [formatters]\n\n        if self.function_code not in [3, 4, 23]:\n            print_formatted_text(HTML(\"<red>Decoder works only for registers!!</red>\"))\n            return\n        byte_order = (\n            Endian.Little if byte_order.strip().lower() == \"little\" else Endian.Big\n        )\n        word_order = (\n            Endian.Little if word_order.strip().lower() == \"little\" else Endian.Big\n        )\n        decoder = BinaryPayloadDecoder.fromRegisters(\n            self.data.get(\"registers\"), byteorder=byte_order, wordorder=word_order\n        )\n        for formatter in formatters:\n            if not (formatter := FORMATTERS.get(formatter)):\n                print_formatted_text(\n                    HTML(f\"<red>Invalid Formatter - {formatter}!!</red>\")\n                )\n                return\n            decoded = getattr(decoder, formatter)()\n            self.print_result(decoded)\n\n    def raw(self):\n        \"\"\"Return raw result dict.\"\"\"\n        self.print_result()\n\n    def _process_dict(self, use_dict):\n        \"\"\"Process dict.\"\"\"\n        new_dict = OrderedDict()\n        for k, v_item in use_dict.items():\n            if isinstance(v_item, bytes):\n                v_item = v_item.decode(\"utf-8\")\n            elif isinstance(v_item, dict):\n                v_item = self._process_dict(v_item)\n            elif isinstance(v_item, (list, tuple)):\n                v_item = [\n                    v1.decode(\"utf-8\") if isinstance(v1, bytes) else v1 for v1 in v_item\n                ]\n            new_dict[k] = v_item\n        return new_dict\n\n    def print_result(self, data=None):\n        \"\"\"Print result object pretty.\n\n        :param data: Data to be printed.\n        \"\"\"\n        data = data or self.data\n        if isinstance(data, dict):\n            data = self._process_dict(data)\n        elif isinstance(data, (list, tuple)):\n            data = [v.decode(\"utf-8\") if isinstance(v, bytes) else v for v in data]\n        elif isinstance(data, bytes):\n            data = data.decode(\"utf-8\")\n        tokens = list(pygments.lex(json.dumps(data, indent=4), lexer=JsonLexer()))\n        print_formatted_text(PygmentsTokens(tokens))\n", "path": "pymodbus/repl/client/helper.py"}], "after_files": [{"content": "\"\"\"Helper Module for REPL actions.\"\"\"\nimport inspect\n\n# pylint: disable=missing-type-doc\nimport json\nfrom collections import OrderedDict\nfrom typing import Any, Dict, List, Union\n\nimport pygments\nfrom prompt_toolkit import print_formatted_text\nfrom prompt_toolkit.formatted_text import HTML, PygmentsTokens\nfrom pygments.lexers.data import JsonLexer\n\nfrom pymodbus.payload import BinaryPayloadDecoder, Endian\n\n\npredicate = inspect.isfunction\nargspec = inspect.signature\n\n\nFORMATTERS = {\n    \"int8\": \"decode_8bit_int\",\n    \"int16\": \"decode_16bit_int\",\n    \"int32\": \"decode_32bit_int\",\n    \"int64\": \"decode_64bit_int\",\n    \"uint8\": \"decode_8bit_uint\",\n    \"uint16\": \"decode_16bit_uint\",\n    \"uint32\": \"decode_32bit_uint\",\n    \"uint64\": \"decode_64bit_int\",\n    \"float16\": \"decode_16bit_float\",\n    \"float32\": \"decode_32bit_float\",\n    \"float64\": \"decode_64bit_float\",\n}\n\n\nDEFAULT_KWARGS = {\"slave\": \"Slave address\"}\n\nOTHER_COMMANDS = {\n    \"result.raw\": \"Show RAW Result\",\n    \"result.decode\": \"Decode register response to known formats\",\n}\nEXCLUDE = [\"execute\", \"recv\", \"send\", \"trace\", \"set_debug\"]\nCLIENT_METHODS = [\n    \"connect\",\n    \"close\",\n    \"idle_time\",\n    \"is_socket_open\",\n    \"get_port\",\n    \"set_port\",\n    \"get_stopbits\",\n    \"set_stopbits\",\n    \"get_bytesize\",\n    \"set_bytesize\",\n    \"get_parity\",\n    \"set_parity\",\n    \"get_baudrate\",\n    \"set_baudrate\",\n    \"get_timeout\",\n    \"set_timeout\",\n    \"get_serial_settings\",\n]\nCLIENT_ATTRIBUTES: List[str] = []\n\n\nclass Command:\n    \"\"\"Class representing Commands to be consumed by Completer.\"\"\"\n\n    def __init__(self, name, signature, doc, slave=False):\n        \"\"\"Initialize.\n\n        :param name: Name of the command\n        :param signature: inspect object\n        :param doc: Doc string for the command\n        :param slave: Use slave as additional argument in the command .\n        \"\"\"\n        self.name = name\n        self.doc = doc.split(\"\\n\") if doc else \" \".join(name.split(\"_\"))\n        self.help_text = self._create_help()\n        self.param_help = self._create_arg_help()\n        if signature:\n            self._params = signature.parameters\n            self.args = self.create_completion()\n        else:\n            self._params = \"\"\n\n        if self.name.startswith(\"client.\") and slave:\n            self.args.update(**DEFAULT_KWARGS)\n\n    def _create_help(self):\n        \"\"\"Create help.\"\"\"\n        doc = filter(lambda d: d, self.doc)\n        cmd_help = list(\n            filter(\n                lambda x: not x.startswith(\":param\") and not x.startswith(\":return\"),\n                doc,\n            )\n        )\n        return \" \".join(cmd_help).strip()\n\n    def _create_arg_help(self):\n        \"\"\"Create arg help.\"\"\"\n        param_dict = {}\n        params = list(filter(lambda d: d.strip().startswith(\":param\"), self.doc))\n        for param in params:\n            param, param_help = param.split(\":param\")[1].strip().split(\":\")\n            param_dict[param] = param_help\n        return param_dict\n\n    def create_completion(self):\n        \"\"\"Create command completion meta data.\n\n        :return:\n        \"\"\"\n        words = {}\n\n        def _create(entry, default):\n            if entry not in [\"self\", \"kwargs\"]:\n                if isinstance(default, (int, str)):\n                    entry += f\"={default}\"\n                return entry\n            return None\n\n        for arg in self._params.values():\n            if entry := _create(arg.name, arg.default):\n                entry, meta = self.get_meta(entry)\n                words[entry] = meta\n\n        return words\n\n    def get_completion(self):\n        \"\"\"Get a list of completions.\n\n        :return:\n        \"\"\"\n        return self.args.keys()\n\n    def get_meta(self, cmd):\n        \"\"\"Get Meta info of a given command.\n\n        :param cmd: Name of command.\n        :return: Dict containing meta info.\n        \"\"\"\n        cmd = cmd.strip()\n        cmd = cmd.split(\"=\")[0].strip()\n        return cmd, self.param_help.get(cmd, \"\")\n\n    def __str__(self):\n        \"\"\"Return string representation.\"\"\"\n        if self.doc:\n            return \"Command {:>50}{:<20}\".format(  # pylint: disable=consider-using-f-string\n                self.name, self.doc\n            )\n        return f\"Command {self.name}\"\n\n\ndef _get_requests(members):\n    \"\"\"Get requests.\"\"\"\n    commands = list(\n        filter(\n            lambda x: (\n                x[0] not in EXCLUDE and x[0] not in CLIENT_METHODS and callable(x[1])\n            ),\n            members,\n        )\n    )\n    commands = {\n        f\"client.{c[0]}\": Command(\n            f\"client.{c[0]}\", argspec(c[1]), inspect.getdoc(c[1]), slave=False\n        )\n        for c in commands\n        if not c[0].startswith(\"_\")\n    }\n    return commands\n\n\ndef _get_client_methods(members):\n    \"\"\"Get client methods.\"\"\"\n    commands = list(\n        filter(lambda x: (x[0] not in EXCLUDE and x[0] in CLIENT_METHODS), members)\n    )\n    commands = {\n        f\"client.{c[0]}\": Command(\n            f\"client.{c[0]}\", argspec(c[1]), inspect.getdoc(c[1]), slave=False\n        )\n        for c in commands\n        if not c[0].startswith(\"_\")\n    }\n    return commands\n\n\ndef _get_client_properties(members):\n    \"\"\"Get client properties.\"\"\"\n    global CLIENT_ATTRIBUTES  # pylint: disable=global-variable-not-assigned\n    commands = list(filter(lambda x: not callable(x[1]), members))\n    commands = {\n        f\"client.{c[0]}\": Command(f\"client.{c[0]}\", None, \"Read Only!\", slave=False)\n        for c in commands\n        if (not c[0].startswith(\"_\") and isinstance(c[1], (str, int, float)))\n    }\n    CLIENT_ATTRIBUTES.extend(list(commands.keys()))\n    return commands\n\n\ndef get_commands(client):\n    \"\"\"Retrieve all required methods and attributes.\n\n    Of a client object and convert it to commands.\n\n    :param client: Modbus Client object.\n    :return:\n    \"\"\"\n    commands = {}\n    members = inspect.getmembers(client)\n    requests = _get_requests(members)\n    client_methods = _get_client_methods(members)\n    client_attr = _get_client_properties(members)\n\n    result_commands = inspect.getmembers(Result, predicate=predicate)\n    result_commands = {\n        f\"result.{c[0]}\": Command(f\"result.{c[0]}\", argspec(c[1]), inspect.getdoc(c[1]))\n        for c in result_commands\n        if (not c[0].startswith(\"_\") and c[0] != \"print_result\")\n    }\n    commands.update(requests)\n    commands.update(client_methods)\n    commands.update(client_attr)\n    commands.update(result_commands)\n    return commands\n\n\nclass Result:\n    \"\"\"Represent result command.\"\"\"\n\n    function_code: int = None\n    data: Union[Dict[int, Any], Any] = None\n\n    def __init__(self, result):\n        \"\"\"Initialize.\n\n        :param result: Response of a modbus command.\n        \"\"\"\n        if isinstance(result, dict):  # Modbus response\n            self.function_code = result.pop(\"function_code\", None)\n            self.data = dict(result)\n        else:\n            self.data = result\n\n    def decode(self, formatters, byte_order=\"big\", word_order=\"big\"):\n        \"\"\"Decode the register response to known formatters.\n\n        :param formatters: int8/16/32/64, uint8/16/32/64, float32/64\n        :param byte_order: little/big\n        :param word_order: little/big\n        \"\"\"\n        # Read Holding Registers (3)\n        # Read Input Registers (4)\n        # Read Write Registers (23)\n        if not isinstance(formatters, (list, tuple)):\n            formatters = [formatters]\n\n        if self.function_code not in [3, 4, 23]:\n            print_formatted_text(HTML(\"<red>Decoder works only for registers!!</red>\"))\n            return\n        byte_order = (\n            Endian.Little if byte_order.strip().lower() == \"little\" else Endian.Big\n        )\n        word_order = (\n            Endian.Little if word_order.strip().lower() == \"little\" else Endian.Big\n        )\n        decoder = BinaryPayloadDecoder.fromRegisters(\n            self.data.get(\"registers\"), byteorder=byte_order, wordorder=word_order\n        )\n        for formatter in formatters:\n            if not (formatter := FORMATTERS.get(formatter)):\n                print_formatted_text(\n                    HTML(f\"<red>Invalid Formatter - {formatter}!!</red>\")\n                )\n                return\n            decoded = getattr(decoder, formatter)()\n            self.print_result(decoded)\n\n    def raw(self):\n        \"\"\"Return raw result dict.\"\"\"\n        self.print_result()\n\n    def _process_dict(self, use_dict):\n        \"\"\"Process dict.\"\"\"\n        new_dict = OrderedDict()\n        for k, v_item in use_dict.items():\n            if isinstance(v_item, bytes):\n                v_item = v_item.decode(\"utf-8\")\n            elif isinstance(v_item, dict):\n                v_item = self._process_dict(v_item)\n            elif isinstance(v_item, (list, tuple)):\n                v_item = [\n                    v1.decode(\"utf-8\") if isinstance(v1, bytes) else v1 for v1 in v_item\n                ]\n            new_dict[k] = v_item\n        return new_dict\n\n    def print_result(self, data=None):\n        \"\"\"Print result object pretty.\n\n        :param data: Data to be printed.\n        \"\"\"\n        data = data or self.data\n        if isinstance(data, dict):\n            data = self._process_dict(data)\n        elif isinstance(data, (list, tuple)):\n            data = [v.decode(\"utf-8\") if isinstance(v, bytes) else v for v in data]\n        elif isinstance(data, bytes):\n            data = data.decode(\"utf-8\")\n        tokens = list(pygments.lex(json.dumps(data, indent=4), lexer=JsonLexer()))\n        print_formatted_text(PygmentsTokens(tokens))\n", "path": "pymodbus/repl/client/helper.py"}]}