asyncmy icon indicating copy to clipboard operation
asyncmy copied to clipboard

Asyncmy does not get the correct host and port using "read_default_file"

Open pgamez opened this issue 2 years ago • 2 comments

Hi,

I've tried to start a connection with asyncmy taking the connection parameters from a config file.

This is my config file (bug.cnf):

[client]
user = myuser
host = myhost
password = mypassword
database = mydatabase

I've prepared this script to show the problem:

#!/usr/bin/env python3

import asyncio

from asyncmy import connect


async def main():
    print("# Using asyncmy directly")
    try:
        conn = await connect(
            host="myhost",
            user="myuser",
            password="mypassword",
            database="mydatabase",
        )
    except Exception as e:
        print(e)

    print("# Using asyncmy directly with a config file")
    try:
        conn = await connect(read_default_file="bug.cnf")
    except Exception as e:
        print(e)


if __name__ == "__main__":
    asyncio.run(main())

This is the output of the script:

./bug.py 
# Using asyncmy directly
(2003, "Can't connect to MySQL server on 'myhost' ([Errno -2] Name or service not known)")
# Using asyncmy directly with a config file
(1045, "Access denied for user 'myuser'@'localhost' (using password: YES)")

You can see that:

  • In the first try, asyncmy tries to connect to host=myhost (correct)
  • In the second try, asyncmy tries to connect to user=myuser (correct) and host=localhost (wrong)

The reason of this bug is placed in the object Connection (connection.pyx). We have this function to read the data from the config file:

def _config(key, arg):
    if arg:
        return arg
    try:
        return cfg.get(read_default_group, key)
    except Exception:
        return arg

Note that the value of arg has a higher priority over the value obtained from the config file, which is requested only when arg is evaluated as False.

Then, we execute this function setting arg to the defaults:

host = _config("host", host)
port = int(_config("port", port))

but host and port defaults ('localhost' and 3306 respectively) are not evaluated to "False", so the corresponding values won't be readed from file:

def __init__(
    self,
    *,
    user=None,  # The first four arguments is based on DB-API 2.0 recommendation.
    password="",
    host='localhost',
    database=None,
    unix_socket=None,
    port=3306,
    ...
):

The solution is to set these defaults to:

  • host=None
  • port=0

In PyMySQL this bug is already fixed:

def __init__(
    self,
    ...
    host=None,
    ...
    port=0,
...
):
    ...
    self.host = host or "localhost"
    self.port = port or 3306
    ...

Thanks and greetings,

Pedro

pgamez avatar Jul 26 '23 05:07 pgamez

Thanks for report! PR welcome.

long2ice avatar Jul 27 '23 02:07 long2ice

Done! Thank you :)

pgamez avatar Jul 27 '23 05:07 pgamez