prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  AsyncioModbusSerialClient TypeError Coroutine
  ### Versions
  
  * Python: 3.9
  * OS: Ubuntu 20.04
  * Pymodbus: `3.0.0dev4`
  * Modbus Hardware (if used): 
  
  ### Pymodbus Specific
  * Server: None
  * Client: rtu - async
  
  ### Description
  
  When I try  `3.0.0dev4` and the latest commit as of today. I am getting a type error that variable `coro` is not a coroutine in file `serial.py`. I am trying to create  `AsyncModbusSerialClient(schedulers.ASYNC_IO, port=connPort, baudrate=connSpeed, method=connMethod, timeout=commTimeout)` in an existing running loop.
  
  I don't think the coroutine was created correctly. What do you think?
  
  Old:
  `future = asyncio.run_coroutine_threadsafe(coro, loop=loop)` 
  
  Proposed:
  ` future = asyncio.run_coroutine_threadsafe(coro(), loop=loop)`
     """Create asyncio based asynchronous serial clients.
      :param port:  Serial port
      :param framer: Modbus Framer
      :param kwargs: Serial port options
      :return: asyncio event loop and serial client
      """
      try:
          loop = kwargs.pop("loop", None) or asyncio.get_running_loop()
      except RuntimeError:
          loop = asyncio.new_event_loop()
  
      proto_cls = kwargs.get("proto_cls") or ModbusClientProtocol
  
      client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)
      coro = client.connect
      if not loop.is_running():
          loop.run_until_complete(coro())
      else:  # loop is not asyncio.get_event_loop():
          future = asyncio.run_coroutine_threadsafe(coro, loop=loop) <- `Fails here`
          future.result()
  
      return loop, client
  ```
  ``` py
  def async_io_factory(port=None, framer=None, **kwargs):
      """Create asyncio based asynchronous serial clients.
      :param port:  Serial port
      :param framer: Modbus Framer
      :param kwargs: Serial port options
      :return: asyncio event loop and serial client
      """
      try:
          loop = kwargs.pop("loop", None) or asyncio.get_running_loop()
      except RuntimeError:
          loop = asyncio.new_event_loop()
  
      proto_cls = kwargs.get("proto_cls") or ModbusClientProtocol
  
      client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)
      coro = client.connect
      if not loop.is_running():
          loop.run_until_complete(coro())
      else:  # loop is not asyncio.get_event_loop():
          future = asyncio.run_coroutine_threadsafe(coro, loop=loop) <- `Fails here`
          future.result()
  
      return loop, client
  ```
  
  
  --- 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/client/asynchronous/factory/serial.py`
  Content:
  ```
  1 """Factory to create asynchronous serial clients based on twisted/asyncio."""
  2 # pylint: disable=missing-type-doc
  3 import logging
  4 import asyncio
  5 
  6 from pymodbus.client.asynchronous import schedulers
  7 from pymodbus.client.asynchronous.thread import EventLoopThread
  8 from pymodbus.client.asynchronous.async_io import (
  9     ModbusClientProtocol,
  10     AsyncioModbusSerialClient,
  11 )
  12 from pymodbus.factory import ClientDecoder
  13 
  14 
  15 _logger = logging.getLogger(__name__)
  16 
  17 
  18 def reactor_factory(port, framer, **kwargs):
  19     """Create twisted serial asynchronous client.
  20 
  21     :param port: Serial port
  22     :param framer: Modbus Framer
  23     :param kwargs:
  24     :return: event_loop_thread and twisted serial client
  25     """
  26     from twisted.internet import reactor  # pylint: disable=import-outside-toplevel
  27     from twisted.internet.serialport import (  # pylint: disable=import-outside-toplevel
  28         SerialPort,
  29     )
  30     from twisted.internet.protocol import (  # pylint: disable=import-outside-toplevel
  31         ClientFactory,
  32     )
  33 
  34     class SerialClientFactory(ClientFactory):
  35         """Define serial client factory."""
  36 
  37         def __init__(self, framer, proto_cls):
  38             """Remember things necessary for building a protocols."""
  39             self.proto_cls = proto_cls
  40             self.framer = framer
  41 
  42         def buildProtocol(self):  # pylint: disable=arguments-differ
  43             """Create a protocol and start the reading cycle-"""
  44             proto = self.proto_cls(self.framer)
  45             proto.factory = self
  46             return proto
  47 
  48     class SerialModbusClient(SerialPort):  # pylint: disable=abstract-method
  49         """Define serial client."""
  50 
  51         def __init__(self, framer, *args, **kwargs):
  52             """Initialize the client and start listening on the serial port.
  53 
  54             :param factory: The factory to build clients with
  55             """
  56             self.decoder = ClientDecoder()
  57             proto_cls = kwargs.pop("proto_cls", None)
  58             proto = SerialClientFactory(framer, proto_cls).buildProtocol()
  59             SerialPort.__init__(self, proto, *args, **kwargs)
  60 
  61     proto = EventLoopThread(
  62         "reactor",
  63         reactor.run,  # pylint: disable=no-member
  64         reactor.stop,  # pylint: disable=no-member
  65         installSignalHandlers=0,
  66     )
  67     ser_client = SerialModbusClient(framer, port, reactor, **kwargs)
  68 
  69     return proto, ser_client
  70 
  71 
  72 def async_io_factory(port=None, framer=None, **kwargs):
  73     """Create asyncio based asynchronous serial clients.
  74 
  75     :param port:  Serial port
  76     :param framer: Modbus Framer
  77     :param kwargs: Serial port options
  78     :return: asyncio event loop and serial client
  79     """
  80     try:
  81         loop = kwargs.pop("loop", None) or asyncio.get_running_loop()
  82     except RuntimeError:
  83         loop = asyncio.new_event_loop()
  84 
  85     proto_cls = kwargs.get("proto_cls") or ModbusClientProtocol
  86 
  87     client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)
  88     coro = client.connect
  89     if not loop.is_running():
  90         loop.run_until_complete(coro())
  91     else:  # loop is not asyncio.get_event_loop():
  92         future = asyncio.run_coroutine_threadsafe(coro, loop=loop)
  93         future.result()
  94 
  95     return loop, client
  96 
  97 
  98 def get_factory(scheduler):
  99     """Get protocol factory based on the backend scheduler being used.
  100 
  101     :param scheduler: REACTOR/ASYNC_IO
  102     :return:
  103     :raises Exception: Failure
  104     """
  105     if scheduler == schedulers.REACTOR:
  106         return reactor_factory
  107     if scheduler == schedulers.ASYNC_IO:
  108         return async_io_factory
  109 
  110     txt = f"Allowed Schedulers: {schedulers.REACTOR}, {schedulers.ASYNC_IO}"
  111     _logger.warning(txt)
  112     txt = f'Invalid Scheduler "{scheduler}"'
  113     raise Exception(txt)
  114
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/pymodbus/client/asynchronous/factory/serial.py b/pymodbus/client/asynchronous/factory/serial.py
  --- a/pymodbus/client/asynchronous/factory/serial.py
  +++ b/pymodbus/client/asynchronous/factory/serial.py
  @@ -89,7 +89,7 @@
       if not loop.is_running():
           loop.run_until_complete(coro())
       else:  # loop is not asyncio.get_event_loop():
  -        future = asyncio.run_coroutine_threadsafe(coro, loop=loop)
  +        future = asyncio.run_coroutine_threadsafe(coro(), loop=loop)
           future.result()
   
       return loop, client
  
verification_info: {"golden_diff": "diff --git a/pymodbus/client/asynchronous/factory/serial.py b/pymodbus/client/asynchronous/factory/serial.py\n--- a/pymodbus/client/asynchronous/factory/serial.py\n+++ b/pymodbus/client/asynchronous/factory/serial.py\n@@ -89,7 +89,7 @@\n     if not loop.is_running():\n         loop.run_until_complete(coro())\n     else:  # loop is not asyncio.get_event_loop():\n-        future = asyncio.run_coroutine_threadsafe(coro, loop=loop)\n+        future = asyncio.run_coroutine_threadsafe(coro(), loop=loop)\n         future.result()\n \n     return loop, client\n", "issue": "AsyncioModbusSerialClient TypeError Coroutine\n### Versions\r\n\r\n* Python: 3.9\r\n* OS: Ubuntu 20.04\r\n* Pymodbus: `3.0.0dev4`\r\n* Modbus Hardware (if used): \r\n\r\n### Pymodbus Specific\r\n* Server: None\r\n* Client: rtu - async\r\n\r\n### Description\r\n\r\nWhen I try  `3.0.0dev4` and the latest commit as of today. I am getting a type error that variable `coro` is not a coroutine in file `serial.py`. I am trying to create  `AsyncModbusSerialClient(schedulers.ASYNC_IO, port=connPort, baudrate=connSpeed, method=connMethod, timeout=commTimeout)` in an existing running loop.\r\n\r\nI don't think the coroutine was created correctly. What do you think?\r\n\r\nOld:\r\n`future = asyncio.run_coroutine_threadsafe(coro, loop=loop)` \r\n\r\nProposed:\r\n` future = asyncio.run_coroutine_threadsafe(coro(), loop=loop)`\r\n   \"\"\"Create asyncio based asynchronous serial clients.\r\n    :param port:  Serial port\r\n    :param framer: Modbus Framer\r\n    :param kwargs: Serial port options\r\n    :return: asyncio event loop and serial client\r\n    \"\"\"\r\n    try:\r\n        loop = kwargs.pop(\"loop\", None) or asyncio.get_running_loop()\r\n    except RuntimeError:\r\n        loop = asyncio.new_event_loop()\r\n\r\n    proto_cls = kwargs.get(\"proto_cls\") or ModbusClientProtocol\r\n\r\n    client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)\r\n    coro = client.connect\r\n    if not loop.is_running():\r\n        loop.run_until_complete(coro())\r\n    else:  # loop is not asyncio.get_event_loop():\r\n        future = asyncio.run_coroutine_threadsafe(coro, loop=loop) <- `Fails here`\r\n        future.result()\r\n\r\n    return loop, client\r\n```\r\n``` py\r\ndef async_io_factory(port=None, framer=None, **kwargs):\r\n    \"\"\"Create asyncio based asynchronous serial clients.\r\n    :param port:  Serial port\r\n    :param framer: Modbus Framer\r\n    :param kwargs: Serial port options\r\n    :return: asyncio event loop and serial client\r\n    \"\"\"\r\n    try:\r\n        loop = kwargs.pop(\"loop\", None) or asyncio.get_running_loop()\r\n    except RuntimeError:\r\n        loop = asyncio.new_event_loop()\r\n\r\n    proto_cls = kwargs.get(\"proto_cls\") or ModbusClientProtocol\r\n\r\n    client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)\r\n    coro = client.connect\r\n    if not loop.is_running():\r\n        loop.run_until_complete(coro())\r\n    else:  # loop is not asyncio.get_event_loop():\r\n        future = asyncio.run_coroutine_threadsafe(coro, loop=loop) <- `Fails here`\r\n        future.result()\r\n\r\n    return loop, client\r\n```\r\n\n", "before_files": [{"content": "\"\"\"Factory to create asynchronous serial clients based on twisted/asyncio.\"\"\"\n# pylint: disable=missing-type-doc\nimport logging\nimport asyncio\n\nfrom pymodbus.client.asynchronous import schedulers\nfrom pymodbus.client.asynchronous.thread import EventLoopThread\nfrom pymodbus.client.asynchronous.async_io import (\n    ModbusClientProtocol,\n    AsyncioModbusSerialClient,\n)\nfrom pymodbus.factory import ClientDecoder\n\n\n_logger = logging.getLogger(__name__)\n\n\ndef reactor_factory(port, framer, **kwargs):\n    \"\"\"Create twisted serial asynchronous client.\n\n    :param port: Serial port\n    :param framer: Modbus Framer\n    :param kwargs:\n    :return: event_loop_thread and twisted serial client\n    \"\"\"\n    from twisted.internet import reactor  # pylint: disable=import-outside-toplevel\n    from twisted.internet.serialport import (  # pylint: disable=import-outside-toplevel\n        SerialPort,\n    )\n    from twisted.internet.protocol import (  # pylint: disable=import-outside-toplevel\n        ClientFactory,\n    )\n\n    class SerialClientFactory(ClientFactory):\n        \"\"\"Define serial client factory.\"\"\"\n\n        def __init__(self, framer, proto_cls):\n            \"\"\"Remember things necessary for building a protocols.\"\"\"\n            self.proto_cls = proto_cls\n            self.framer = framer\n\n        def buildProtocol(self):  # pylint: disable=arguments-differ\n            \"\"\"Create a protocol and start the reading cycle-\"\"\"\n            proto = self.proto_cls(self.framer)\n            proto.factory = self\n            return proto\n\n    class SerialModbusClient(SerialPort):  # pylint: disable=abstract-method\n        \"\"\"Define serial client.\"\"\"\n\n        def __init__(self, framer, *args, **kwargs):\n            \"\"\"Initialize the client and start listening on the serial port.\n\n            :param factory: The factory to build clients with\n            \"\"\"\n            self.decoder = ClientDecoder()\n            proto_cls = kwargs.pop(\"proto_cls\", None)\n            proto = SerialClientFactory(framer, proto_cls).buildProtocol()\n            SerialPort.__init__(self, proto, *args, **kwargs)\n\n    proto = EventLoopThread(\n        \"reactor\",\n        reactor.run,  # pylint: disable=no-member\n        reactor.stop,  # pylint: disable=no-member\n        installSignalHandlers=0,\n    )\n    ser_client = SerialModbusClient(framer, port, reactor, **kwargs)\n\n    return proto, ser_client\n\n\ndef async_io_factory(port=None, framer=None, **kwargs):\n    \"\"\"Create asyncio based asynchronous serial clients.\n\n    :param port:  Serial port\n    :param framer: Modbus Framer\n    :param kwargs: Serial port options\n    :return: asyncio event loop and serial client\n    \"\"\"\n    try:\n        loop = kwargs.pop(\"loop\", None) or asyncio.get_running_loop()\n    except RuntimeError:\n        loop = asyncio.new_event_loop()\n\n    proto_cls = kwargs.get(\"proto_cls\") or ModbusClientProtocol\n\n    client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)\n    coro = client.connect\n    if not loop.is_running():\n        loop.run_until_complete(coro())\n    else:  # loop is not asyncio.get_event_loop():\n        future = asyncio.run_coroutine_threadsafe(coro, loop=loop)\n        future.result()\n\n    return loop, client\n\n\ndef get_factory(scheduler):\n    \"\"\"Get protocol factory based on the backend scheduler being used.\n\n    :param scheduler: REACTOR/ASYNC_IO\n    :return:\n    :raises Exception: Failure\n    \"\"\"\n    if scheduler == schedulers.REACTOR:\n        return reactor_factory\n    if scheduler == schedulers.ASYNC_IO:\n        return async_io_factory\n\n    txt = f\"Allowed Schedulers: {schedulers.REACTOR}, {schedulers.ASYNC_IO}\"\n    _logger.warning(txt)\n    txt = f'Invalid Scheduler \"{scheduler}\"'\n    raise Exception(txt)\n", "path": "pymodbus/client/asynchronous/factory/serial.py"}], "after_files": [{"content": "\"\"\"Factory to create asynchronous serial clients based on twisted/asyncio.\"\"\"\n# pylint: disable=missing-type-doc\nimport logging\nimport asyncio\n\nfrom pymodbus.client.asynchronous import schedulers\nfrom pymodbus.client.asynchronous.thread import EventLoopThread\nfrom pymodbus.client.asynchronous.async_io import (\n    ModbusClientProtocol,\n    AsyncioModbusSerialClient,\n)\nfrom pymodbus.factory import ClientDecoder\n\n\n_logger = logging.getLogger(__name__)\n\n\ndef reactor_factory(port, framer, **kwargs):\n    \"\"\"Create twisted serial asynchronous client.\n\n    :param port: Serial port\n    :param framer: Modbus Framer\n    :param kwargs:\n    :return: event_loop_thread and twisted serial client\n    \"\"\"\n    from twisted.internet import reactor  # pylint: disable=import-outside-toplevel\n    from twisted.internet.serialport import (  # pylint: disable=import-outside-toplevel\n        SerialPort,\n    )\n    from twisted.internet.protocol import (  # pylint: disable=import-outside-toplevel\n        ClientFactory,\n    )\n\n    class SerialClientFactory(ClientFactory):\n        \"\"\"Define serial client factory.\"\"\"\n\n        def __init__(self, framer, proto_cls):\n            \"\"\"Remember things necessary for building a protocols.\"\"\"\n            self.proto_cls = proto_cls\n            self.framer = framer\n\n        def buildProtocol(self):  # pylint: disable=arguments-differ\n            \"\"\"Create a protocol and start the reading cycle-\"\"\"\n            proto = self.proto_cls(self.framer)\n            proto.factory = self\n            return proto\n\n    class SerialModbusClient(SerialPort):  # pylint: disable=abstract-method\n        \"\"\"Define serial client.\"\"\"\n\n        def __init__(self, framer, *args, **kwargs):\n            \"\"\"Initialize the client and start listening on the serial port.\n\n            :param factory: The factory to build clients with\n            \"\"\"\n            self.decoder = ClientDecoder()\n            proto_cls = kwargs.pop(\"proto_cls\", None)\n            proto = SerialClientFactory(framer, proto_cls).buildProtocol()\n            SerialPort.__init__(self, proto, *args, **kwargs)\n\n    proto = EventLoopThread(\n        \"reactor\",\n        reactor.run,  # pylint: disable=no-member\n        reactor.stop,  # pylint: disable=no-member\n        installSignalHandlers=0,\n    )\n    ser_client = SerialModbusClient(framer, port, reactor, **kwargs)\n\n    return proto, ser_client\n\n\ndef async_io_factory(port=None, framer=None, **kwargs):\n    \"\"\"Create asyncio based asynchronous serial clients.\n\n    :param port:  Serial port\n    :param framer: Modbus Framer\n    :param kwargs: Serial port options\n    :return: asyncio event loop and serial client\n    \"\"\"\n    try:\n        loop = kwargs.pop(\"loop\", None) or asyncio.get_running_loop()\n    except RuntimeError:\n        loop = asyncio.new_event_loop()\n\n    proto_cls = kwargs.get(\"proto_cls\") or ModbusClientProtocol\n\n    client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)\n    coro = client.connect\n    if not loop.is_running():\n        loop.run_until_complete(coro())\n    else:  # loop is not asyncio.get_event_loop():\n        future = asyncio.run_coroutine_threadsafe(coro(), loop=loop)\n        future.result()\n\n    return loop, client\n\n\ndef get_factory(scheduler):\n    \"\"\"Get protocol factory based on the backend scheduler being used.\n\n    :param scheduler: REACTOR/ASYNC_IO\n    :return:\n    :raises Exception: Failure\n    \"\"\"\n    if scheduler == schedulers.REACTOR:\n        return reactor_factory\n    if scheduler == schedulers.ASYNC_IO:\n        return async_io_factory\n\n    txt = f\"Allowed Schedulers: {schedulers.REACTOR}, {schedulers.ASYNC_IO}\"\n    _logger.warning(txt)\n    txt = f'Invalid Scheduler \"{scheduler}\"'\n    raise Exception(txt)\n", "path": "pymodbus/client/asynchronous/factory/serial.py"}]}