Почему Python вызывает исключение, несмотря на пустой блок Except? - PullRequest
0 голосов
/ 22 января 2020

Представьте себе следующий код:

def query(sql, data):
    with conn as cursor:
        try:
            cursor.execute(sql, data)
            rows = cursor.fetchall()
            conn.commit()
        except Exception as e:
            print(cursor._last_executed)
            print(e)

При его вызове err.InterfaceError("(0, '')") поднимается с последней строки: print(e).

Я мог бы даже понять, если бы это было поднялся с print(cursor._last_executed), так как cursor может быть недоступен или что-то в этом роде. Но это не тот случай.

Почему, когда мое голое исключение должно обрабатывать все?

Пожалуйста, не обращайте внимания на обсуждение, является ли голое исключение хорошей или плохой практикой, это еще одна топика c. Вопрос в том, почему исключение вообще возникает в этом случае.

Edit : исключение возникает очень редко, при большой нагрузке на БД. Вы не сможете его воспроизвести.

Edit2 : мне удалось скопировать трассировку в виде текста из часового отчета:

InterfaceError: (0, '')
  File "run_signal_generator.py", line 39, in <module>
    main()
  File "run_signal_generator.py", line 35, in main
    ds.run_trades_stream()
  File "/home/deribit/rubber-band/data_stream/data_streamer.py", line 223, in run_trades_stream
    self.process_data(data)
  File "/home/deribit/rubber-band/data_stream/data_streamer.py", line 97, in process_data
    self.start_new_candle(timeframe)
  File "/home/deribit/rubber-band/data_stream/data_streamer.py", line 117, in start_new_candle
    self.notify()
  File "/home/deribit/rubber-band/data_stream/observer.py", line 13, in notify
    observer.update()
  File "/home/deribit/rubber-band/data_stream/observer.py", line 26, in update
    self.process_data()
  File "/home/deribit/rubber-band/data_stream/signal_generator.py", line 131, in process_data
    return self.process_potential_new_trade()
  File "/home/deribit/rubber-band/data_stream/signal_generator.py", line 160, in process_potential_new_trade
    return self.process_enter_signal()
  File "/home/deribit/rubber-band/data_stream/signal_generator.py", line 407, in process_enter_signal
    trade_id = self.store_enter_signal_db(data)
  File "/home/deribit/rubber-band/data_stream/signal_generator.py", line 522, in store_enter_signal_db
    return query(sql, db_data)["id"]
  File "/home/deribit/rubber-band/helpers/mysql.py", line 19, in query
    print(e)
  File "pymysql/connections.py", line 881, in __exit__
    self.commit()
  File "pymysql/connections.py", line 798, in commit
    self._execute_command(COMMAND.COM_QUERY, "COMMIT")
  File "pymysql/connections.py", line 1122, in _execute_command
    raise err.InterfaceError("(0, '')")

Действительно, он требует исключения восстает из этой линии.

Ответы [ 2 ]

3 голосов
/ 22 января 2020

Похоже на этот старый вопрос : mysql соединения и курсоры не являются поточно-ориентированными, поэтому, если вы разделяете соединения между несколькими потоками (вместо одного соединения на поток), их состояние может нарушиться по существу в любой момент.

Возможно, что по какой-то причине __str__ при ошибках соединения MySQL пропингует сервер, чтобы попытаться получить дополнительную информацию, которая прерывается, если, например, другой поток уже изменил соединение штат. cursor._last_executed может не иметь такой проблемы из-за простого сохранения информации локально либо при выполнении запроса, либо когда ответ возвращается.

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

1 голос
/ 22 января 2020

Понял.

Вот что происходит:

1) Основное query вызывает ошибку.

2) Обрабатывается.

3) Поскольку query находится внутри диспетчера контекста, диспетчер контекста закрывает свой объект, например, экземпляр соединения.

4) Когда сервер MySQL ушел, __exit__ метод класса соединения не может выполняться должным образом.

5) Так как он не может выполняться должным образом, он вызывает ошибку в рамках диспетчера контекста , выходящем за рамки простой обработки исключение внутри query.

Вы можете получить тот же результат, если поэкспериментируете со следующим кодом:

class ContextManagerException(Exception):
    pass


class TryBlockException(Exception):
    pass


class A(object):
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, tb):
        raise ContextManagerException


with A() as a:
    try:
        raise TryBlockException
        pass
    except Exception as e:
        print(e)

Как видите, возникшее исключение действительно вызывается из диспетчера контекста. и действительно, оно взято из строки print(e), так как это последняя строка для выполнения из диспетчера контекста.

...