Search code examples
pythonpython-3.xpython-decoratorspython-tenacity

Referencing self in decorator


I am implementing a database connector class in python. I will use the retry decorator from tenacity library to retry the connection of database when it times out.

I want to pass the self.retry_count and self.retry_interval to the arguments in retry decorator.

## etl_connect.py
from sqlalchemy import create_engine
import pymysql
import logging
from tenacity import *

class Connector():
    def __init__(self, mode, conn_str, retry_count, retry_interval):
        self.mode = mode
        self.conn_str = conn_str
        self.retry_count = retry_count
        self.retry_interval = retry_interval
        self.engine = None
        self.conn = None

    @retry(wait=wait_fixed(self.retry_interval), stop=stop_after_attempt(self.retry_count))
    def mysql_connect(self):
        logging.info('Connecting to mysql. (retry count=%d)' % (self.mysql_connect.retry.statistics['attempt_number']))
        mysql_engine = create_engine(self.conn_str)
        mysql_conn = mysql_engine.connect()
        logging.info('Connected to mysql successfully with %d attempt(s).' % (self.mysql_connect.retry.statistics['attempt_number']))
        return (mysql_engine, mysql_conn)

Now call the mysql_connect function:

## call.py
from etl_connect import *
mysql_connector = Connector('mysql', 'mysql database string here', 5, 10)
engine, conn = mysql_connector.mysql_connect()

But it shows: NameError: name 'self' is not defined.

Traceback (most recent call last):
  File "call.py", line 5, in <module>
    from etl_connect import *
  File "/home/developer/ETL_modules/etl_connect.py", line 19, in <module>
    class Connector():
  File "/home/developer/ETL_modules/etl_connect.py", line 56, in Connector
    @retry(wait=wait_fixed(self.retry_interval), stop=stop_after_attempt(self.retry_count))
NameError: name 'self' is not defined

Are there any ways that I can pass self.retry_count & self.retry_interval to the decorator?


Solution

  • Instead of decorating the method, call retry when you call the method.

    ## etl_connect.py
    from sqlalchemy import create_engine
    import pymysql
    import logging
    from tenacity import *
    
    class Connector():
        def __init__(self, mode, conn_str, retry_count, retry_interval):
            self.mode = mode
            self.conn_str = conn_str
            self.retry_count = retry_count
            self.retry_interval = retry_interval
            self.engine = None
            self.conn = None
    
        def _mysql_connect(self):
            logging.info('Connecting to mysql. (retry count=%d)' % (self.mysql_connect.retry.statistics['attempt_number']))
            mysql_engine = create_engine(self.conn_str)
            mysql_conn = mysql_engine.connect()
            logging.info('Connected to mysql successfully with %d attempt(s).' % (self.mysql_connect.retry.statistics['attempt_number']))
            return (mysql_engine, mysql_conn)
    
        def mysql_connect(self):
            d = retry(
                  wait=wait_fixed(self.retry_interval),
                  stop=stop_after_attempt(self.retry_count)
                )
            # One of these two should work, depending on how
            # retry is actually defined.
            return d(Connector._mysql_connect)(self)
            # return d(self._mysql_connect)