Python : 메서드 호출에서 발생할 수있는 예외를 어떻게 알 수 있습니까?


87

파이썬 코드를 실행할 때 예상되는 예외를 (코딩 할 때) 아는 방법이 있습니까? 나는 어떤 예외 유형이 던져 질 수 있는지 모르기 때문에 90 %의 시간 동안 기본 Exception 클래스를 잡게됩니다. 문서가 업데이트되거나 정확하지 않은 경우). 이것을 확인하는 도구가 있습니까? (파이썬 코드와 라이브러리를 읽는 것과 같이)?


2
Python <2.6에서는 하위 클래스 raise뿐 아니라 문자열도 사용할 수 있습니다 BaseException. 따라서 제어 할 수없는 라이브러리 코드를 호출하는 경우 except Exception문자열 예외를 포착하지 않기 때문에 충분하지 않습니다. 다른 사람들이 지적했듯이 여기에서 잘못된 나무를 짖고 있습니다.
Daniel Pryden

나는 그것을 몰랐다. 나는 예외를 제외하고 생각했다 : .. 거의 모든 것을 잡는다.
GabiMe

2
except ExceptionPython 2.6 이상에서 문자열 예외를 포착하는 데 잘 작동합니다.
Jeffrey Harris

답변:


22

정적 타이핑 규칙이 없기 때문에 해결책이 정확하지 않을 수 있다고 생각합니다.

예외를 확인하는 도구에 대해서는 잘 모르지만, 자신의 필요에 맞는 도구를 찾을 수 있습니다 (정적 분석을 약간 사용할 수있는 좋은 기회).

첫 번째 시도로 AST를 빌드하고 모든 Raise노드를 찾은 다음 예외를 발생시키는 일반적인 패턴을 파악 하는 함수를 작성할 수 있습니다 (예 : 생성자를 직접 호출).

하자 x다음과 같은 프로그램이 될 :

x = '''\
if f(x):
    raise IOError(errno.ENOENT, 'not found')
else:
    e = g(x)
    raise e
'''

compiler패키지를 사용하여 AST를 빌드합니다 .

tree = compiler.parse(x)

그런 다음 Raise방문자 클래스를 정의하십시오 .

class RaiseVisitor(object):
    def __init__(self):
        self.nodes = []
    def visitRaise(self, n):
        self.nodes.append(n)

그리고 AST 수집 Raise노드를 살펴 봅니다 .

v = RaiseVisitor()
compiler.walk(tree, v)

>>> print v.nodes
[
    Raise(
        CallFunc(
            Name('IOError'),
            [Getattr(Name('errno'), 'ENOENT'), Const('not found')],
            None, None),
        None, None),
    Raise(Name('e'), None, None),
]

컴파일러 기호 테이블을 사용하여 기호를 해결하고 데이터 종속성을 분석하는 등 계속할 수 있습니다. 또는 CallFunc(Name('IOError'), ...)"확실히 제기하는 것을 의미해야 합니다 "라고 추론 할 수 있습니다 IOError. 이는 빠른 실제 결과를 위해 매우 좋습니다. :)


이 흥미로운 답변에 감사드립니다. 왜 모든 인상 노드보다 더 다른 것을 찾아야하는지 이해하지 못했습니다. 왜 "컴파일러 기호 테이블을 사용하여 기호를 해결하고 데이터 종속성을 분석"해야합니까? 예외를 발생시키는 유일한 방법은 raise ()입니까?
GabiMe

1
v.nodes위 의 값이 주어지면 실제로 어떤 것이 Name('IOError')또는 무엇인지 말할 수 없습니다 Name('e'). 소위 자유 변수이기 때문에 그 값 IOErrore가리킬 수 있는 값을 알 수 없습니다 . 바인딩 컨텍스트가 알려졌더라도 (여기서는 기호 테이블이 작동 함) 정확한 값을 추론하기 위해 일종의 데이터 종속성 분석을 수행해야합니다 (파이썬에서는 어렵습니다).
Andrey Vlasovskikh

실용적인 반자동 솔루션을 찾고 ['IOError(errno.ENOENT, "not found")', 'e']있으므로 사용자에게 표시 되는 목록은 괜찮습니다. 하지만 당신은 변수의 값의 실제 클래스 (죄송합니다 다시 게시 용) 문자열 :)으로 표시 할 수없는 추론
안드레이 Vlasovskikh에게

1
예. 이 방법은 영리하지만 실제로 완전한 커버리지를 제공하지는 않습니다. Python의 동적 특성으로 인해 .NET Framework와 같은 작업을 수행하기 위해 호출하는 코드에 대해 완벽하게 가능합니다 (분명히 나쁜 생각이지만) exc_class = raw_input(); exec "raise " + exc_class. 요점은 이러한 종류의 정적 분석이 Python과 같은 동적 언어에서는 실제로 가능하지 않다는 것입니다.
Daniel Pryden

7
그건 그렇고, 당신은 할 수 find /path/to/library -name '*.py' | grep 'raise '비슷한 결과를 :) 얻을
안드레이 Vlasovskikh

24

처리 할 예외 만 포착해야합니다.

구체적인 유형별로 모든 예외를 포착하는 것은 말도 안됩니다. 당신은 당신이 특정 예외를 잡을해야 할 수합니다 처리합니다. 다른 예외의 경우, "기본 예외"를 포착하고이를 기록 ( str()함수 사용 )하고 프로그램을 종료하는 (또는 충돌 상황에 적합한 다른 작업을 수행 하는) 일반적인 catch를 작성할 수 있습니다 .

실제로 모든 예외를 처리 하고 치명적인 예외가 없다고 확신하는 경우 (예 : 샌드 박스 환경에서 코드를 실행하는 경우) 일반적인 BaseException을 포착하는 방법이 목표에 적합합니다.

사용중인 라이브러리에 대한 참조 가 아닌 언어 예외 참조 에도 관심이있을 수 있습니다 .

라이브러리 참조가 정말 열악하고 시스템 참조를 잡을 때 자체 예외를 다시 발생시키지 않는 경우 유일한 유용한 접근 방식은 테스트를 실행 하는 것입니다 (문서화되지 않은 경우 변경 될 수 있으므로 테스트 스위트에 추가 할 수 있습니다!). . 코드에 중요한 파일을 삭제하고 어떤 예외가 발생하는지 확인하십시오. 너무 많은 데이터를 제공하고 어떤 오류가 발생하는지 확인하십시오.

어쨌든 테스트를 실행 해야 할 것입니다 . "필요한 파일을 찾을 수 없습니다!"라는 오류 메시지가 표시 될 수 있습니다. 잡을 때 IndexError? 테스트 만이 알 수 있습니다.


26
물론입니다.하지만 어떤 예외가 던져 질지 모르는 경우 처리해야 할 예외를 어떻게 결정할 수 있습니까?
GabiMe

@ bugspy.net,이 문제를 반영하기 위해 내 답변 수정
P Shved

아마도 이것을 알아낼 수있는 소스 코드 분석기가 필요한 것일까 요? 개발하기 위해 너무 열심히해야하지 내가 생각
GabiMe

@ bugspy.net, 나는 왜 그럴 시간이 아닌지 절을 대담하게 표현했습니다.
P Shved 2010 년

물론 당신이 옳습니다. 그러나 개발 중에 어떤 유형의 예외가 발생할 수 있는지 아는 것은 여전히 ​​흥미로울 수 있습니다.
hek2mgl

11

이 문제를 해결하는 올바른 도구는 단위 테스트입니다. unittest에서 발생하지 않는 실제 코드에서 예외가 발생하는 경우 더 많은 unittest가 필요합니다.

이걸 고려하세요

def f(duck):
    try:
        duck.quack()
    except ??? could be anything

오리는 모든 객체가 될 수 있습니다.

분명히 AttributeErrorif duck에 돌팔이가 없을 수 있고 TypeErrorif duck에는 돌팔이가 있지만 호출 할 수 없습니다. 당신은 무슨 생각이 없다 duck.quack()하지만 인상, 어쩌면 수 DuckError또는 무언가를

이제 다음과 같은 코드가 있다고 가정합니다.

arr[i] = get_something_from_database()

a IndexError가 발생하면 arr [i]에서 왔는지 아니면 데이터베이스 함수 내부에서 왔는지 알 수 없습니다. 일반적으로 예외가 발생한 위치는 그다지 중요하지 않습니다. 오히려 무언가 잘못되었고 원하는 일이 발생하지 않았습니다.

편리한 기술은 다음과 같이 예외를 포착하고 다시 발생시키는 것입니다.

except Exception as e
    #inspect e, decide what to do
    raise

당신이 그것을 "리 레이즈"하려고한다면 왜 그것을 잡아야합니까?
Tarnay Kálmán

당신은하지 않습니다 주석을 표시하기 위해 가정 된 것입니다, 그것을 리 레이즈 할 수 있습니다.
John La Rooy

2
당신은 또한 다음 리 레이즈 예외 어딘가에 기록하도록 선택할 수 있습니다
존 라 Rooy

2
단위 테스트를 작성하는 것이 답이라고 생각하지 않습니다. 질문은 "예상 할 예외를 어떻게 찾을 수 있는지"이며 단위 테스트를 작성해도이 문제를 찾는 데 도움이되지 않습니다. 사실, 단위 테스트를 작성하려면 어떤 예외가 예상되는지 이미 알고 있어야하므로 올바른 단위 테스트를 작성하려면 원래 질문도 답해야합니다.
Bruno Ranschaert 19

6

지금까지 아무도 설명하지 않았고, 100 % 정확한 예외 목록을 가질 수없는 이유를 설명하지 않았으므로 이에 대해 언급 할 가치가 있다고 생각했습니다. 그 이유 중 하나는 일류 기능입니다. 다음과 같은 기능이 있다고 가정 해 보겠습니다.

def apl(f,arg):
   return f(arg)

이제 apl발생하는 모든 예외를 발생시킬 수 있습니다 f. 핵심 라이브러리에는 이와 같은 기능이 많지 않지만 사용자 정의 필터, 맵, 축소 등과 함께 목록 이해를 사용하는 모든 기능이 영향을받습니다.

문서와 소스 분석기는 여기서 유일하게 "심각한"정보 소스입니다. 그들이 할 수없는 것을 명심하십시오.


4

나는 소켓을 사용할 때 이것을 만났고, 내가 실행할 모든 오류 조건을 찾고 싶었습니다 (그래서 오류를 만들고 어떤 소켓이 간결한 목록을 원했는지 알아내는 대신). 궁극적으로 "raise"에 대해 "/usr/lib64/python2.4/test/test_socket.py"를 grep'ing했습니다.

$ grep raise test_socket.py
Any exceptions raised by the clients during their tests
        raise TypeError, "test_func must be a callable function"
    raise NotImplementedError, "clientSetUp must be implemented."
    def raise_error(*args, **kwargs):
        raise socket.error
    def raise_herror(*args, **kwargs):
        raise socket.herror
    def raise_gaierror(*args, **kwargs):
        raise socket.gaierror
    self.failUnlessRaises(socket.error, raise_error,
    self.failUnlessRaises(socket.error, raise_herror,
    self.failUnlessRaises(socket.error, raise_gaierror,
        raise socket.error
    # Check that setting it to an invalid value raises ValueError
    # Check that setting it to an invalid type raises TypeError
    def raise_timeout(*args, **kwargs):
    self.failUnlessRaises(socket.timeout, raise_timeout,
    def raise_timeout(*args, **kwargs):
    self.failUnlessRaises(socket.timeout, raise_timeout,

꽤 간결한 오류 목록입니다. 물론 이것은 사례별로 만 작동하며 테스트가 정확한지에 따라 다릅니다 (보통 그렇습니다). 그렇지 않으면 거의 모든 예외를 잡아 내고 로그하고 분석하고 처리 방법을 파악해야합니다 (단위 테스트에서는 어렵지 않습니다).


4
이것은 파이썬에서 예외 처리가 매우 문제가된다는 주장을 강화합니다. 만약 우리가 아주 기본적인 것을 다루기 위해 grep 또는 소스 분석기를 사용해야한다면 (예를 들어 자바에서는 처음부터 존재했습니다. 때로는 장황함이 좋은 것입니다. 자바는 장황합니다. 그러나 적어도 불쾌한 놀라움은 없습니다)
GabiMe

@GabiMe,이 능력 (또는 일반적으로 정적 타이핑)이 모든 버그를 방지하는 은색 총알과는 다릅니다. 자바는 끔찍한 놀라움 으로 가득 차 있습니다. 이것이 Eclipse가 정기적으로 충돌하는 이유입니다.
John La Rooy 2013-08-16

2

내가 유익하다고 생각한 두 가지 방법이 있습니다. 첫 번째는 iPython에서 코드를 실행하여 예외 유형을 표시합니다.

n = 2
str = 'me '
str + 2
TypeError: unsupported operand type(s) for +: 'int' and 'str'

두 번째 방법으로 우리는 너무 많이 잡는 것에 안주하고 시간이 지남에 따라 개선합니다. try코드에 표현식을 포함하고 except Exception as err. 어떤 예외가 발생했는지 알기 위해 충분한 데이터를 인쇄하십시오. 예외가 발생하면 더 정확한 except절 을 추가하여 코드를 개선하십시오 . 모든 관련 예외를 포착했다고 생각되면 모든 예외를 제거하십시오. 어쨌든 프로그래밍 오류를 삼키기 때문에 좋은 일입니다.

try:
   so something
except Exception as err:
   print "Some message"
   print err.__class__
   print err
   exit(1)

1

일반적으로 몇 줄의 코드에서만 예외를 포착하면됩니다. 전체 main기능을 try except절 에 넣고 싶지 않을 것 입니다. 몇 줄마다 어떤 종류의 예외가 발생할 수 있는지 항상 (또는 쉽게 확인할 수 있어야합니다).

문서에는 기본 제공 예외 의 전체 목록이 있습니다. 예상하지 못한 예외를 제외하려고하지 마십시오. 호출 코드에서 처리 / 예상 될 수 있습니다.

편집 : 던질 수있는 것은 분명히 당신이하는 일에 달려 있습니다! 시퀀스의 임의 요소에 액세스 : IndexError, dict의 임의 요소 : KeyError

IDLE에서 몇 줄을 실행하고 예외를 발생 시키십시오. 그러나 unittest는 당연히 더 나은 해결책이 될 것입니다.


1
이것은 내 간단한 질문에 대답하지 않습니다. 예외 처리를 설계하는 방법이나 포착시기 또는 방법에 대해서는 묻지 않습니다. I가 발생 될 수 무엇을 찾는 방법을 물어
GabiMe

1
@ bugspy.net : 요청한 것을 수행하는 것은 불가능 하며 이것은 완벽하게 유효한 해결 방법입니다.
Daniel Pryden
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.