Ваша проблема связана с исключениями, возникающими при вызове метода __del__
. Как указано здесь ,
Из-за сомнительных обстоятельств, при которых вызываются методы del (), исключения, возникающие во время их выполнения, игнорируются, и Вместо этого в sys.stderr выводится предупреждение
Вот почему ваш блок исключений, кстати, не работает ...
Вы можете проверить это с помощью mcve:
# warntest.py
class Foo(object):
def __del__(self):
raise ValueError("YADDA")
f = Foo()
затем
$ python3 warntest.py
Exception ignored in: <bound method Foo.__del__ of <__main__.Foo object at 0x7efcb61dc898>>
Traceback (most recent call last):
File "warntest.py", line 3, in __del__
ValueError: YADDA
Несмотря на то, что может предложить «напечатанное предупреждение» и к моему большому разочарованию, просто запрос Python к предупреждений о тишине ничего не меняет здесь - python3 -Wignore warntest.py
ведет себя так же, и ручная настройка фильтра «игнорировать» в скрипте не делает больше.
Я боюсь, здесь нет простого, но чистого решения. Это оставляет вам три возможных варианта:
1 / исправить проблему у источника. graph-tool - это OSS, вы можете внести свой вклад, улучшив эти __del__
методы, чтобы они не вызывали никаких исключений.
2 / используйте хак. Можно обернуть этот вызов менеджером contextlib.redirect_stderr
- я пытался, он работает, как и ожидалось, но он не позволит абсолютно ничего достичь stderr во время этого вызова.
3 / жить с ним ...
РЕДАКТИРОВАТЬ
Я попытался найти источник GT и не могу найти, где это исключение
Он написан в виде обычного текста в отосланном вами трекбеке ... Собственно, само исключение возникает в gi/overrides/__init__.py
, но не в этом суть - вы хотите отредактировать GraphWindow.__del__
(и GraphWidget.__del__
слишком FWIW), чтобы обернуть вызовы self.graph.cleanup()
(self.cleanup()
в GraphWidget
в самом грубом из возможных блоков try / кроме. Следующий MCVE воспроизводит проблему:
class Sub(object):
def cleanup(self):
raise ValueError("YADDA")
def __del__(self):
self.cleanup()
class Main(object):
def __init__(self):
self.sub = Sub()
def __del__(self):
self.sub.cleanup()
Main()
И исправить это довольно просто:
class Sub(object):
def cleanup(self):
raise ValueError("YADDA")
def __del__(self):
# yes, a bare except clause and a pass...
# this is exactly what you're NOT supposed to do,
# never ever, because it's BAD... but here it's
# ok - provided you double-checked what the code
# in the `try` block really do, of course.
try:
self.cleanup()
except:
pass
class Main(object):
def __init__(self):
self.sub = Sub()
def __del__(self):
try:
self.sub.cleanup()
except:
pass
Важное примечание : этот тип обработчика исключений (за исключением простого предложения, за которым следует pass
) равен точно что один должен никогда, никогда делать. Это самое ужасное исключение при обработке antipattern. Но __del__
действительно особый случай, и в текущем случае метод cleanup()
только пытается отменить регистрацию функции обратного вызова так что это действительно безвредно.
но даже при перенаправлении вывод в файл
Обратите внимание, что вы хотите перенаправить stderr
, а не stdout
. У меня работает следующий фрагмент:
import contextlib
class Foo(object):
def __del__(self):
raise ValueError("YADDA")
def test():
Foo()
with contextlib.redirect_stderr(io.StringIO()):
test()