# Buggy code
def async_io_factory(
    host="127.0.0.1",
    port=Defaults.Port,
    framer=None,
    source_address=None,
    timeout=None,
    **kwargs,
):
    """
    Factory to create asyncio based asynchronous tcp clients
    :param host: Host IP address
    :param port: Port
    :param framer: Modbus Framer
    :param source_address: Bind address
    :param timeout: Timeout in seconds
    :param kwargs:
    :return: asyncio event loop and tcp client
    """
    import asyncio
    from pymodbus.client.asynchronous.asyncio import init_tcp_client

    loop = kwargs.get("loop") or asyncio.new_event_loop()
    proto_cls = kwargs.get("proto_cls", None)
    if not loop.is_running():
        asyncio.set_event_loop(loop)
        cor = init_tcp_client(proto_cls, loop, host, port)
        client = loop.run_until_complete(asyncio.gather(cor))[0]
    else:
        cor = init_tcp_client(proto_cls, loop, host, port)
        future = asyncio.run_coroutine_threadsafe(cor, loop=loop)
        client = future.result()

    return loop, client


# Traceback (AttributeError)
DEBUG    transaction    :115      Current transaction state - IDLE
DEBUG    transaction    :120      Running transaction 1
DEBUG    transaction    :219      SEND: 0x1 0x2b 0xe 0x2 0x4 0x71 0x44
DEBUG    sync           :75       New Transaction state 'SENDING'
DEBUG    transaction    :228      Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG    transaction    :304      Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
DEBUG    transaction    :233      RECV: 0x1 0x2b 0xe 0x2 0x83 0x0 0x0 0x2 0x4 0xd 0x23 0x31 0x20 0x54 0x65 0x73 0x74 0x20 0x53 0x6c 0x61 0x76 0x65 0x5 0x10 0x52 0x65 0x73 0x65 0x61 0x72 0x63 0x68 0x20 0x54 0x65 0x73 0x74 0x62 0x65 0x64 0xca 0xb
DEBUG    rtu_framer     :180      Getting Frame - 0x2b 0xe 0x2 0x83 0x0 0x0 0x2 0x4 0xd 0x23 0x31 0x20 0x54 0x65 0x73 0x74 0x20 0x53 0x6c 0x61 0x76 0x65 0x5 0x10 0x52 0x65 0x73 0x65 0x61 0x72 0x63 0x68 0x20 0x54 0x65 0x73 0x74 0x62 0x65 0x64
DEBUG    factory        :266      Factory Response[ReadDeviceInformationResponse: 43]
DEBUG    rtu_framer     :115      Frame advanced, resetting header!!
DEBUG    transaction    :383      Adding transaction 1
DEBUG    transaction    :394      Getting transaction 1
DEBUG    transaction    :193      Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
{u'function_code': 43, u'information': {4: '#1 Test Slave', 5: 'Research Testbed'}, u'space left': None, u'object count': 2, u'next object id': 0, u'more follows': 0, u'conformity': 131}
DEBUG    transaction    :115      Current transaction state - TRANSACTION_COMPLETE
DEBUG    transaction    :120      Running transaction 2
DEBUG    transaction    :219      SEND: 0x1 0x10 0x0 0x1 0x0 0x3 0x6 0x0 0x2 0x0 0x3 0x0 0x4 0x3f 0x46
DEBUG    rtu_framer     :264      Changing state to IDLE - Last Frame End - 1595356416.71, Current Time stamp - 1595356416.81
DEBUG    sync           :75       New Transaction state 'SENDING'
DEBUG    transaction    :228      Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG    transaction    :304      Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
DEBUG    transaction    :233      RECV: 0x1 0x10 0x0 0x1 0x0 0x3 0xd1 0xc8
DEBUG    rtu_framer     :180      Getting Frame - 0x10 0x0 0x1 0x0 0x3
DEBUG    factory        :266      Factory Response[WriteMultipleRegistersResponse: 16]
DEBUG    rtu_framer     :115      Frame advanced, resetting header!!
DEBUG    transaction    :383      Adding transaction 1
DEBUG    transaction    :394      Getting transaction 1
DEBUG    transaction    :193      Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
{u'count': 3, u'function_code': 16, u'address': 1}
DEBUG    transaction    :115      Current transaction state - TRANSACTION_COMPLETE
DEBUG    transaction    :120      Running transaction 3
DEBUG    transaction    :219      SEND: 0x0 0x10 0x0 0x1 0x0 0x3 0x6 0x0 0x2 0x0 0x3 0x0 0x4 0x3d 0xc7
DEBUG    rtu_framer     :264      Changing state to IDLE - Last Frame End - 1595356416.89, Current Time stamp - 1595356416.99
DEBUG    sync           :75       New Transaction state 'SENDING'
DEBUG    transaction    :223      Changing transaction state from 'SENDING' to 'TRANSACTION_COMPLETE'
Traceback (most recent call last):
File "repl_broadcast.py", line 30, in <module>
print(client.write_registers(1, [2, 3, 4], unit=0))
File "/home/pi/.local/lib/python2.7/site-packages/pymodbus/repl/client.py", line 172, in write_registers
if not resp.isError():
AttributeError: 'str' object has no attribute 'isError'

# Fixed code
def async_io_factory(
    host="127.0.0.1",
    port=Defaults.Port,
    framer=None,
    source_address=None,
    timeout=None,
    **kwargs,
):
    """
    Factory to create asyncio based asynchronous tcp clients
    :param host: Host IP address
    :param port: Port
    :param framer: Modbus Framer
    :param source_address: Bind address
    :param timeout: Timeout in seconds
    :param kwargs:
    :return: asyncio event loop and tcp client
    """
    import asyncio
    from pymodbus.client.asynchronous.async_io import init_tcp_client

    loop = kwargs.get("loop") or asyncio.new_event_loop()
    proto_cls = kwargs.get("proto_cls", None)
    if not loop.is_running():
        asyncio.set_event_loop(loop)
        cor = init_tcp_client(proto_cls, loop, host, port)
        client = loop.run_until_complete(asyncio.gather(cor))[0]
    else:
        cor = init_tcp_client(proto_cls, loop, host, port)
        future = asyncio.run_coroutine_threadsafe(cor, loop=loop)
        client = future.result()

    return loop, client