Search code examples
pythonvisual-studio-codeconnectionpython-decorators

100% Working Python decorator DB Connector shows errors in VS Code


I have created a decorator to handle database connections like this:

def with_connection(f):
  def with_connection_(args, **kwargs):

    conn = mysql.connector.Connect("my redacted credentials")
    try:
        result = f(conn, args, **kwargs)
    except:
        conn.rollback()
        print("SQL failed")
        raise
    else:
        conn.commit()
    finally:
        conn.close()
    return result
return with_connection_

I call it like this:

@with_connection
def GetUsername(conn, args):
    Cursor = conn.cursor()
    query = "select user_name from users where user_id = %s"
    Cursor.execute(query, (args[0],))
    result = Cursor.fetchone()
    return result

user_name = DBQueries.GetUsername(user_id)

This code works just fine.

However, in VS Code, I get the following error reported, and I feel I should understand why it is happening:

"message": "No value for argument 'args' in function call",

I kind of get it, because GetUsername has 2 arguments, conn & args and I call it with one, so it thinks it is missing the args argument. But it WORKS.

Can anyone clarify for me what is going on here?


Solution

  • Your IDE is basically confused because GetUsername indeed expects different arguments. In some IDE's this problem can be solved by wrapping the inner function with @functools.wraps(f). (https://docs.python.org/3/library/functools.html#functools.wraps). This basically takes proper care of the meta information of the wrapped function so that it resembles the information of the original function.

    Another approach you can take (but I would still suggest using functools.wraps as well for this method) is taking the connection argument from the kwargs like this:

    def with_connection(f):
        def with_connection_(*args, **kwargs):
            conn = mysql.connector.Connect("my redacted credentials")
            try:
                result = f(*args, connection=conn, **kwargs)
            except:
                conn.rollback()
                print("SQL failed")
                raise
            else:
                conn.commit()
            finally:
                conn.close()
            return result
        return with_connection_
    
    @with_connection
    def GetUsername(*args, **kwargs):
        conn = kwargs.pop("connection")
        Cursor = conn.cursor()
        query = "select user_name from users where user_id = %s"
        Cursor.execute(query, (args[0],))
        result = Cursor.fetchone()
        return result
    

    There's also not really a need to do args[0], you could just do:

    @with_connection
    def GetUsername(user_id, *args, **kwargs):
        conn = kwargs.pop("connection")
        Cursor = conn.cursor()
        query = "select user_name from users where user_id = %s"
        Cursor.execute(query, (user_id,))
        result = Cursor.fetchone()
        return result