2016-11-04 1 views
9

--pdb 명령을 ipython과 함께 사용하므로 코드를 디버깅 할 때 오류가 발생하면 스택 추적을 표시합니다. 이러한 많은 오류는 잘못된 입력으로 numpy 또는 pandas 함수를 호출 할 때 발생합니다. 스택 추적은 이러한 라이브러리의 코드에서 최신 프레임부터 시작합니다. 나중에 up 명령을 5-10 번 반복하면 내가 실제로 잘못한 것을 실제로 볼 수 있습니다. 즉, 90 %의 시간이 즉시 명백해질 것입니다 (예 : 배열 대신 목록으로 호출).예외 발생 후 가장 오래된 스택 프레임에서 파이썬 디버거를 시작하십시오.

디버거가 처음 시작할 스택 프레임을 지정하는 방법이 있습니까? 가장 오래된 스택 프레임 또는 파이썬 파일의 최신 스택 프레임이 처음 실행되거나 유사합니다. 이것은 디버깅에 훨씬 더 생산적 일 것입니다. 여기

간단한 예 추적을 생성

import pandas as pd 

def test(df): # (A) 
    df[:,0] = 4 #Bad indexing on dataframe, will cause error 
    return df 

df = test(pd.DataFrame(range(3))) # (B) 

, (A), (B)의 (C)는 이제 이상적으로, I는에서 시작하는 디버거하고자 명료

In [6]: --------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 
<ipython-input-6-66730543fac0> in <module>() 
----> 1 import codecs, os;__pyfile = codecs.open('''/tmp/py29142W1d''', encoding='''utf-8''');__code = __pyfile.read().encode('''utf-8''');__pyfile.close();os.remove('''/tmp/py29142W1d''');exec(compile(__code, '''/test/stack_frames.py''', 'exec')); 

/test/stack_frames.py in <module>() 
     6 
     7 if __name__ == '__main__': 
(A)----> 8  df = test(pd.DataFrame(range(3))) 

/test/stack_frames.py in test(df) 
     2 
     3 def test(df): 
(B)----> 4  df[:,0] = 4 
     5  return df 
     6 

/usr/local/lib/python2.7/dist-packages/pandas/core/frame.pyc in __setitem__(self, key, value) 
    2355   else: 
    2356    # set column 
-> 2357    self._set_item(key, value) 
    2358 
    2359  def _setitem_slice(self, key, value): 

/usr/local/lib/python2.7/dist-packages/pandas/core/frame.pyc in _set_item(self, key, value) 
    2421 
    2422   self._ensure_valid_index(value) 
-> 2423   value = self._sanitize_column(key, value) 
    2424   NDFrame._set_item(self, key, value) 
    2425 

/usr/local/lib/python2.7/dist-packages/pandas/core/frame.pyc in _sanitize_column(self, key, value) 
    2602 
    2603   # broadcast across multiple columns if necessary 
-> 2604   if key in self.columns and value.ndim == 1: 
    2605    if (not self.columns.is_unique or 
    2606      isinstance(self.columns, MultiIndex)): 

/usr/local/lib/python2.7/dist-packages/pandas/indexes/base.pyc in __contains__(self, key) 
    1232 
    1233  def __contains__(self, key): 
-> 1234   hash(key) 
    1235   # work around some kind of odd cython bug 
    1236   try: 

TypeError: unhashable type 
> /usr/local/lib/python2.7/dist-packages/pandas/indexes/base.py(1234)__contains__() 
    1232 
    1233  def __contains__(self, key): 
(C)-> 1234   hash(key) 
    1235   # work around some kind of odd cython bug 
    1236   try: 

ipdb> 

추가 (B)에서 두 번째로 오래된 프레임 또는 심지어 (A)에서. 그러나 그것이 기본적으로 어디로가는 지 명확히 (C)는 아닙니다.

+1

http : // stackoverflow.com/questions/37069323/stop-at-exception-in-my-not-library-code가 관련되어있을 수 있습니다. –

답변

2

오랫동안 내 자신을위한 프로세스를 문서화하십시오. 하단에 반 작업 솔루션 : 여기

실패 시도 :

import sys 
import pdb 
import pandas as pd 

def test(df): # (A) 
    df[:,0] = 4 #Bad indexing on dataframe, will cause error 
    return df 

mypdb = pdb.Pdb(skip=['pandas.*']) 
mypdb.reset() 

df = test(pd.DataFrame(range(3))) # (B) # fails. 

mypdb.interaction(None, sys.last_traceback) # doesn't work. 

Pdb skip documentation:

스킵 인수가 주어진 경우, 글로브 스타일의 모듈 이름 패턴의 반복 가능한해야합니다. 디버거는 이러한 패턴 중 하나와 일치하는 모듈에서 시작된 프레임을 실행하지 않습니다.

class Pdb(bdb.Bdb, cmd.Cmd): 

    _previous_sigint_handler = None 

    def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, 
       nosigint=False, readrc=True): 
     bdb.Bdb.__init__(self, skip=skip) 
     [...] 

# Post-Mortem interface 

def post_mortem(t=None): 
    # handling the default 
    if t is None: 
     # sys.exc_info() returns (type, value, traceback) if an exception is 
     # being handled, otherwise it returns None 
     t = sys.exc_info()[2] 
    if t is None: 
     raise ValueError("A valid traceback must be passed if no " 
         "exception is being handled") 

    p = Pdb() 
    p.reset() 
    p.interaction(None, t) 

def pm(): 
    post_mortem(sys.last_traceback) 

Bdb source code:

class Bdb: 
    """Generic Python debugger base class. 
    This class takes care of details of the trace facility; 
    a derived class should implement user interaction. 
    The standard debugger class (pdb.Pdb) is an example. 
    """ 

    def __init__(self, skip=None): 
     self.skip = set(skip) if skip else None 
    [...] 
    def is_skipped_module(self, module_name): 
     for pattern in self.skip: 
      if fnmatch.fnmatch(module_name, pattern): 
       return True 
     return False 

    def stop_here(self, frame): 
     # (CT) stopframe may now also be None, see dispatch_call. 
     # (CT) the former test for None is therefore removed from here. 
     if self.skip and \ 
       self.is_skipped_module(frame.f_globals.get('__name__')): 
      return False 
     if frame is self.stopframe: 
      if self.stoplineno == -1: 
       return False 
      return frame.f_lineno >= self.stoplineno 
     if not self.stopframe: 
      return True 
     return False 

스킵 목록이 후 mortems 사용하지 않을 것이 분명하다 Pdb source code:

. 이 문제를 해결하기 위해 설치 메서드를 재정의하는 사용자 지정 클래스를 만들었습니다.

import pdb 

class SkipPdb(pdb.Pdb): 
    def setup(self, f, tb): 
     # This is unchanged 
     self.forget() 
     self.stack, self.curindex = self.get_stack(f, tb) 
     while tb: 
      # when setting up post-mortem debugging with a traceback, save all 
      # the original line numbers to be displayed along the current line 
      # numbers (which can be different, e.g. due to finally clauses) 
      lineno = pdb.lasti2lineno(tb.tb_frame.f_code, tb.tb_lasti) 
      self.tb_lineno[tb.tb_frame] = lineno 
      tb = tb.tb_next 

     self.curframe = self.stack[self.curindex][0] 
     # This loop is new 
     while self.is_skipped_module(self.curframe.f_globals.get('__name__')): 
      self.curindex -= 1 
      self.stack.pop() 
      self.curframe = self.stack[self.curindex][0] 
     # The rest is unchanged. 
     # The f_locals dictionary is updated from the actual frame 
     # locals whenever the .f_locals accessor is called, so we 
     # cache it here to ensure that modifications are not overwritten. 
     self.curframe_locals = self.curframe.f_locals 
     return self.execRcLines() 

    def pm(self): 
     self.reset() 
     self.interaction(None, sys.last_traceback) 

당신이 사용하는 경우이 같이

x = 42 
df = test(pd.DataFrame(range(3))) # (B) # fails. 
# fails. Then do: 
mypdb = SkipPdb(skip=['pandas.*']) 
mypdb.pm() 
>> <ipython-input-36-e420cf1b80b2>(2)<module>() 
>-> df = test(pd.DataFrame(range(3))) # (B) # fails. 
> (Pdb) l 
> 1 x = 42 
> 2 -> df = test(pd.DataFrame(range(3))) # (B) # fails. 
> [EOF] 

당신은 오른쪽 프레임으로 삭제됩니다. 이제 은 ipython이 자신의 pdb pm/post_mortem 함수를 호출하는 방법을 파악하고 유사한 스크립트를 작성해야합니다. 어느 appears to be hard, 그래서 나는 꽤 많이 포기합니다.

또한 위대한 구현은 아닙니다. 건너 뛸 프레임이 스택 맨 위에 있다고 가정하고 이상한 결과를 생성합니다. 예 : df.apply에 대한 입력 기능의 오류는 매우 이상한 것을 만들어 낼 것입니다.

TLDR : stdlib에서 지원하지 않지만 고유 한 디버거 클래스를 만들 수는 있지만 IPythons 디버거에서 작동하도록하는 것은 중요하지 않습니다.

+0

건너 뛰기 목록을 설정할 때'set_trace'를 호출하는 것이 올바른 구문 인 것처럼 보입니다. 'import pdb; pdb.Pdb (skip = [ 'django. *']). set_trace()' – user2699

+0

그러면 _ 호출 스택에서 디버거를 호출합니다 ._. 그러나 우리는 어디에서 잘못되었는지 마지막 stacktrace를 가져 와서 거기에 디버거를 입력하려고합니다. –

관련 문제