2016-11-04 8 views
9

मैं ipython के साथ --pdb कमांड का उपयोग करता हूं, इसलिए जब मैं डीबगिंग कोड और त्रुटि उत्पन्न करता हूं तो यह एक स्टैक ट्रेस दिखाता है। इनमें से बहुत सी त्रुटियां खराब इनपुट के साथ 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) 

ट्रैस बैक परिणामस्वरूप, (ए), (बी) है, (सी) स्पष्टता के लिए

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> 

जोड़ा अब आदर्श, मैं डिबगर चाहते में शुरू करने के लिए होगा (बी), या यहां तक ​​कि (ए) पर दूसरा सबसे पुराना फ्रेम। लेकिन निश्चित रूप से नहीं (सी) जहां यह डिफ़ॉल्ट रूप से जाता है।

+1

http: // stackoverflow।कॉम/प्रश्न/37069323/स्टॉप-ऑन-अपवाद-इन-मेरे-लाइब्रेरी-कोड से संबंधित नहीं हो सकता है। –

उत्तर

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:

छोड़ तर्क, अगर दिया, ग्लोब शैली मॉड्यूल का नाम पैटर्न के एक iterable होना चाहिए। डीबगर फ्रेम में कदम नहीं उठाएगा जो मॉड्यूल में उत्पन्न होता है जो इन पैटर्नों में से किसी एक से मेल खाता है।

Pdb source code:

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 के लिए इस्तेमाल नहीं कर रहा है। इसे ठीक करने के लिए मैंने एक कस्टम क्लास बनाई जो सेटअप विधि को ओवरराइड करता है।

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 उनके पीडीबी pm/post_mortem फ़ंक्शन को कैसे कॉल कर रहा है, और एक समान स्क्रिप्ट बनाएं। कौन सा appears to be hard, इसलिए मैं यहां बहुत ज्यादा छोड़ देता हूं।

यह भी एक बहुत ही महान कार्यान्वयन नहीं है। यह मानता है कि जिन फ्रेमों को आप छोड़ना चाहते हैं वे आपके ढेर के शीर्ष पर हैं, और अजीब परिणाम अन्यथा उत्पन्न करेंगे। जैसे इनपुट फ़ंक्शन में df.apply में त्रुटि कुछ सुपर अजीब उत्पन्न करेगी।

TLDR: stdlib द्वारा समर्थित नहीं है, लेकिन आप अपने खुद के डिबगर वर्ग बना सकते हैं, लेकिन यह IPythons डिबगर के साथ काम है कि प्राप्त करने के लिए nontrivial है।

+0

यह स्किप सूची सेट करते समय 'set_trace' को कॉल करना सही सिंटैक्स है, उदा। 'आयात पीडीबी; pdb.Pdb (skip = ['django। *'])। set_trace() ' – user2699

+0

वह कॉलिंग स्टैक frame._ पर डीबगर दर्ज करेगा। लेकिन हम आखिरी स्टैकट्रैक लेना चाहते हैं जहां यह गलत हो गया, और वहां डीबगर दर्ज करें। –

संबंधित मुद्दे