Ссылка на себя в декораторе

Я реализую класс коннектора базы данных в python. Я буду использоватьretry декоратор из библиотеки упорства, чтобы повторить попытку подключения к базе данных по истечении времени ожидания.

Я хочу передать self.retry_count а также self.retry_interval к аргументам в retry декоратор.

## 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)

Теперь позвоните в mysql_connect функция:

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

Но это показывает: 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

Есть ли способы пройти? self.retry_count & self.retry_interval декоратору?

4 ответа

Решение

Вместо того, чтобы украшать метод, вызовите retryкогда вы вызываете метод.

## 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)

Если у вас нет доступа @retryдекоратор, и вы не можете его редактировать, вы можете определить переменную вне определения вашего класса. Или вы можете определить эту базовую конфигурацию в файле настроек, а затем импортировать ее. Посмотрите на это:

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


base_config = {
    'retry_count': 3,
    'retry_interval': 30,
    ...
}

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

    @retry(wait=wait_fixed(base_config['retry_interval']), stop=stop_after_attempt(base_config['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)

Или импортировать из файла настроек:

from sqlalchemy import create_engine
import pymysql
import logging
from tenacity import *

from settings import RETRY_COUNT
from settings import RETRY_INTERVAL

Я знаю, что это надежный способ, но эти параметры должны быть определены в ваших настройках, и нет необходимости передавать их каждый раз, когда вы хотите создать экземпляр вашего класса.

Предполагая, что вы не можете легко переопределить упорство retry декоратор, вы можете обернуть его своим собственным, который ссылается на значения в Connector пример.

Вот что я имею в виду:

# Wrapper for tenacity retry decorator
def my_retry(func):
    def wrapped(conn, *args, **kwargs):
        tdecorator = retry(wait=wait_fixed(conn.retry_interval),
                           stop=stop_after_attempt(conn.retry_count))
        decorated = tdecorator(func)
        return decorated(conn, *args, **kwargs)
    return wrapped


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

    @my_retry
    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)

Если декоратор (повтор) не определен как часть класса (и не является статическим), вы не можете ссылаться на экземпляр объекта в нем.

Другие вопросы по тегам