Как я отключаю и затем повторно включаю предупреждение?

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

Я использую Python 2.6, таким образом, я, как предполагается, могу сделать это с catch_warnings менеджером по контексту, но это, кажется, не работает на меня. Даже приводя это к сбою, я должен также смочь назвать resetwarnings и затем сбросить мой фильтр.

Вот простой пример, который иллюстрирует проблему:

>>> import warnings
>>> warnings.simplefilter("error", UserWarning)
>>> 
>>> def f():
...     warnings.warn("Boo!", UserWarning)
... 
>>> 
>>> f() # raises UserWarning as an exception
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 2, in f
UserWarning: Boo!
>>> 
>>> f() # still raises the exception
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 2, in f
UserWarning: Boo!
>>> 
>>> with warnings.catch_warnings():
...     warnings.simplefilter("ignore")
...     f()     # no warning is raised or printed
... 
>>> 
>>> f() # this should raise the warning as an exception, but doesn't
>>> 
>>> warnings.resetwarnings()
>>> warnings.simplefilter("error", UserWarning)
>>> 
>>> f() # even after resetting, I'm still getting nothing
>>> 

Кто-то может объяснить, как я могу выполнить это?

Править: По-видимому, это - известная ошибка: http://bugs.python.org/issue4180

12
задан Eli Courtwright 8 March 2010 в 14:53
поделиться

2 ответа

Перечитав несколько раз документацию и поковырявшись в исходниках и оболочке, я, кажется, разобрался. Документация, вероятно, может быть улучшена, чтобы прояснить поведение.

Модуль warnings ведет реестр __warningsregistry__, чтобы отслеживать, какие предупреждения были показаны. Если предупреждение (сообщение) не указано в реестре до установки фильтра 'error', любые вызовы warn() не приведут к добавлению сообщения в реестр. Кроме того, реестр предупреждений не создается до первого вызова warn:

>>> import warnings
>>> __warningregistry__
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
NameError: name '__warningregistry__' is not defined

>>> warnings.simplefilter('error')
>>> __warningregistry__
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
NameError: name '__warningregistry__' is not defined

>>> warnings.warn('asdf')
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
UserWarning: asdf

>>> __warningregistry__
{}

Теперь, если мы проигнорируем предупреждения, они будут добавлены в реестр предупреждений:

>>> warnings.simplefilter("ignore")
>>> warnings.warn('asdf')
>>> __warningregistry__
{('asdf', <type 'exceptions.UserWarning'>, 1): True}
>>> warnings.simplefilter("error")
>>> warnings.warn('asdf')
>>> warnings.warn('qwerty')
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
UserWarning: qwerty

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

11
ответ дан 2 December 2019 в 05:40
поделиться

Брайан замечен в __ реестре предупреждений __ . Поэтому вам нужно расширить catch_warnings , чтобы сохранить / восстановить глобальный __ warningregistry __

Что-то вроде этого может работать

class catch_warnings_plus(warnings.catch_warnings):
    def __enter__(self):
        super(catch_warnings_plus,self).__enter__()
        self._warningregistry=dict(globals.get('__warningregistry__',{}))
    def __exit__(self, *exc_info):
        super(catch_warnings_plus,self).__exit__(*exc_info)
        __warningregistry__.clear()
        __warningregistry__.update(self._warningregistry)
6
ответ дан 2 December 2019 в 05:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: