2010-03-06 4 views
12

파이썬 라이브러리에 대한 단위 테스트를 작성 중이며 특정 경고가 예외로 발생하도록하고 싶습니다. 이는 simplefilter 함수로 쉽게 할 수 있습니다. 그러나 한 가지 테스트에서는 경고를 사용하지 않고 테스트를 실행 한 다음 경고를 다시 사용하고 싶습니다.경고를 비활성화 한 다음 다시 활성화하려면 어떻게합니까?

저는 파이썬 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 "<stdin>", line 1, in <module> 
    File "<stdin>", line 2, in f 
UserWarning: Boo! 
>>> 
>>> f() # still raises the exception 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", 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

+1

경고 모듈에 약간의 버그가있을 수 있습니다. 나는 셸에서 코드를 가지고 놀고 있었고 경고 메시지가 동일한 다른 함수를 선언한다고해도 경고 (경고 없음)와 동일한 효과가 있었지만 경고 메시지를 변경하면 작동 할 수 있다는 것을 알았습니다. –

답변

11

워드 프로세서와 몇 번을 읽고 내가 그것을 알아 낸 것 같아 소스와 쉘 주위에 파고 : 분명히 이것은 알려진 버그입니다. 문서는 동작이 무엇인지 명확하게하기 위해 향상 될 수 있습니다.

은 경고 모듈은 경고가 표시되어있는 추적 할 __warningsregistry__에서 레지스트리를 유지합니다. 'error'필터가 설정되기 전에 경고 (메시지)가 레지스트리에 나열되지 않으면 warn()을 호출해도 메시지가 레지스트리에 추가되지 않습니다.

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

그래서 오류 : 우리는 경고를 무시하면 이제 그들이 경고 레지스트리에 추가 얻을 것이다

>>> 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__ 
{} 

을 : 또한, 경고 레지스트리를 경고하기 위해 첫 번째 통화까지 만들 수 나타나지 않습니다 필터는 경고 레지스트리에없는 경고에만 적용됩니다. 코드를 작동 시키려면 컨텍스트 관리자를 사용할 때 경고 레지스트리에서 적절한 항목을 지워야합니다 (일반적으로 무시 필터를 사용한 후 언제든지 이전에 사용한 메시지를 원한다면 오류 필터를 가져와야 함). ...

6

브라이언 자리는 __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) 
+0

이 코드의 사용법에 상당히 관심이 있습니다. 이전에 무시 된 경고를 존중합니까? –

8

브라이언 루프트 약 __warningregistry__ 문제의 원인이되는 올바른 작동 할 수 글로벌 __warningregistry__를 저장/복원 할 catch_warnings을 확장 할 필요가있다. 그러나 나는 한 가지를 명확히하고 싶었 다음 warnings 모듈이 작동하는 표시 방법이 warn()가 호출 될 때마다 모듈에 대한 module.__warningregistry__을 설정하는 것입니다. 복잡한 것을 더 복잡하게하는 경고에 대한 stacklevel 옵션은 경고를 "이름으로"발급 한 모듈에 대해 속성을 설정하게합니다 (반드시 warn()이 호출 된 것은 아닙니다 ...). 경고가 발행 된 시간.

즉, __warningregistry__ 속성이있는 곳에 많은 모듈이있을 수 있으며 응용 프로그램에 따라 경고가 다시 나타나기 전에 모두 지워야 할 수도 있습니다. 나는 이것을 달성하기 위해 다음 코드 스 니펫에 의존해왔다.그것은 이름이 정규 표현식 (기본값은 모든)과 일치하는 모든 모듈에 대한 경고 레지스트리를 삭제합니다 :

def reset_warning_registry(pattern=".*"): 
    "clear warning registry for all match modules" 
    import re 
    import sys 
    key = "__warningregistry__" 
    for mod in sys.modules.values(): 
     if hasattr(mod, key) and re.match(pattern, mod.__name__): 
      getattr(mod, key).clear() 

업데이트 : CPython의 issue 21724 주소 문제가 resetwarnings() 명확하지 경고 상태를 않습니다. 이 문제에 대한 확장 된 "컨텍스트 관리자"버전을 첨부했으며 reset_warning_registry.py에서 다운로드 할 수 있습니다. 엘리 콜린스 유용한 설명에 이어

2

여기 콘텍스트 관리자 들어갈 때 모듈의 소정의 순서로 경고 레지스트리를 클리어 catch_warnings 콘텍스트 관리자의 수정 된 버전이고, 출구에서 레지스트리 복구 :

을 같은 뭔가
from warnings import catch_warnings 

class catch_warn_reset(catch_warnings): 
    """ Version of ``catch_warnings`` class that resets warning registry 
    """ 
    def __init__(self, *args, **kwargs): 
     self.modules = kwargs.pop('modules', []) 
     self._warnreg_copies = {} 
     super(catch_warn_reset, self).__init__(*args, **kwargs) 

    def __enter__(self): 
     for mod in self.modules: 
      if hasattr(mod, '__warningregistry__'): 
       mod_reg = mod.__warningregistry__ 
       self._warnreg_copies[mod] = mod_reg.copy() 
       mod_reg.clear() 
     return super(catch_warn_reset, self).__enter__() 

    def __exit__(self, *exc_info): 
     super(catch_warn_reset, self).__exit__(*exc_info) 
     for mod in self.modules: 
      if hasattr(mod, '__warningregistry__'): 
       mod.__warningregistry__.clear() 
      if mod in self._warnreg_copies: 
       mod.__warningregistry__.update(self._warnreg_copies[mod]) 

사용 :

import my_module_raising_warnings 
with catch_warn_reset(modules=[my_module_raising_warnings]): 
    # Whatever you'd normally do inside ``catch_warnings`` 
0

저도 같은 문제로 실행했습니다, 그리고 다른 모든 답변이 유효한 동안은 다른 경로를 선택합니다. 나는 경고 모듈을 테스트하고 싶지 않으며 내부 동작에 대해서도 알지 못한다. 그래서 난 그냥 대신 조롱 :

import warnings 
import unittest 
from unittest.mock import patch 
from unittest.mock import call 

class WarningTest(unittest.TestCase): 
    @patch('warnings.warn') 
    def test_warnings(self, fake_warn): 
     warn_once() 
     warn_twice() 
     fake_warn.assert_has_calls(
      [call("You've been warned."), 
      call("This is your second warning.")]) 

def warn_once(): 
    warnings.warn("You've been warned.") 

def warn_twice(): 
    warnings.warn("This is your second warning.") 

if __name__ == '__main__': 
    __main__=unittest.main() 

이 코드는 unittest.mock은 2.7에 추가 된 2.6 당신은 사용을 외부 조롱 라이브러리를 필요 파이썬 3입니다.

관련 문제