2012-11-15 3 views
11

में एक परिवर्तनीय परिवर्तन के लिए देखें बड़ी पाइथन परियोजना है जहां एक वर्ग की एक विशेषता के पास कुछ जगह पर गलत मूल्य है।पायथन

यह sqlalchemy.orm.attributes.InstrumentedAttribute होना चाहिए, लेकिन जब मैं परीक्षण चलाता हूं तो यह स्थिर मान होता है, चलिए स्ट्रिंग कहें।

डीबग मोड में पायथन प्रोग्राम चलाने का कोई तरीका है, और कोड के प्रत्येक चरण के माध्यम से स्वचालित रूप से कुछ चेक (यदि चर बदल गया प्रकार) चलाएं?

पीएस मुझे पता है कि निरीक्षण और संपत्ति सजावट की सहायता से कक्षा के उदाहरण की विशेषता के परिवर्तन कैसे लॉग इन करें। संभवत: यहाँ मैं metaclasses साथ इस पद्धति का उपयोग कर सकते हैं ...

लेकिन कभी कभी मैं और अधिक सामान्य और शक्तिशाली समाधान की आवश्यकता है ...

धन्यवाद।

पी.पी.एस. मुझे वहां कुछ चाहिए: https://stackoverflow.com/a/7669165/816449, लेकिन उस कोड में क्या हो रहा है इसके बारे में अधिक स्पष्टीकरण के साथ हो सकता है।

उत्तर

11

ठीक है, यहां धीमी दृष्टिकोण का एक प्रकार है। इसे स्थानीय परिवर्तनीय परिवर्तन (केवल नाम से) देखने के लिए संशोधित किया जा सकता है। यहां बताया गया है कि यह कैसे काम करता है: हम sys.settrace करते हैं और प्रत्येक चरण obj.attr के मान का विश्लेषण करते हैं। मुश्किल हिस्सा यह है कि लाइन को निष्पादित करने से पहले हमें 'line' ईवेंट (कुछ पंक्ति निष्पादित की गई) प्राप्त होती है। इसलिए, जब हम देखते हैं कि obj.attr बदल गया है, हम पहले से ही अगली पंक्ति पर हैं और हम पिछले लाइन फ्रेम नहीं प्राप्त कर सकते हैं (क्योंकि प्रत्येक पंक्ति के लिए फ्रेम कॉपी नहीं किए जाते हैं, वे संशोधित होते हैं)। तो प्रत्येक पंक्ति घटना पर मैं traceback.format_stack से watcher.prev_st बचाता हूं और यदि trace_command मान की अगली कॉल पर बदल गया है, तो हम सहेजे गए स्टैक ट्रेस को फ़ाइल में प्रिंट करते हैं। प्रत्येक पंक्ति पर ट्रेसबैक सहेजना काफी महंगा ऑपरेशन है, इसलिए आपको अपनी परियोजनाओं की निर्देशिका (या सिर्फ अपनी परियोजना की जड़) की सूची में include कीवर्ड सेट करना होगा ताकि यह देखने के लिए कि अन्य पुस्तकालय अपनी सामग्री और अपशिष्ट कैसे कर रहे हैं सी पी यू।

watcher.py

import traceback 

class Watcher(object): 
    def __init__(self, obj=None, attr=None, log_file='log.txt', include=[], enabled=False): 
     """ 
      Debugger that watches for changes in object attributes 
      obj - object to be watched 
      attr - string, name of attribute 
      log_file - string, where to write output 
      include - list of strings, debug files only in these directories. 
       Set it to path of your project otherwise it will take long time 
       to run on big libraries import and usage. 
     """ 

     self.log_file=log_file 
     with open(self.log_file, 'wb'): pass 
     self.prev_st = None 
     self.include = [incl.replace('\\','/') for incl in include] 
     if obj: 
      self.value = getattr(obj, attr) 
     self.obj = obj 
     self.attr = attr 
     self.enabled = enabled # Important, must be last line on __init__. 

    def __call__(self, *args, **kwargs): 
     kwargs['enabled'] = True 
     self.__init__(*args, **kwargs) 

    def check_condition(self): 
     tmp = getattr(self.obj, self.attr) 
     result = tmp != self.value 
     self.value = tmp 
     return result 

    def trace_command(self, frame, event, arg): 
     if event!='line' or not self.enabled: 
      return self.trace_command 
     if self.check_condition(): 
      if self.prev_st: 
       with open(self.log_file, 'ab') as f: 
        print >>f, "Value of",self.obj,".",self.attr,"changed!" 
        print >>f,"###### Line:" 
        print >>f,''.join(self.prev_st) 
     if self.include: 
      fname = frame.f_code.co_filename.replace('\\','/') 
      to_include = False 
      for incl in self.include: 
       if fname.startswith(incl): 
        to_include = True 
        break 
      if not to_include: 
       return self.trace_command 
     self.prev_st = traceback.format_stack(frame) 
     return self.trace_command 
import sys 
watcher = Watcher() 
sys.settrace(watcher.trace_command) 

testwatcher.py

from watcher import watcher 
import numpy as np 
import urllib2 
class X(object): 
    def __init__(self, foo): 
     self.foo = foo 

class Y(object): 
    def __init__(self, x): 
     self.xoo = x 

    def boom(self): 
     self.xoo.foo = "xoo foo!" 
def main(): 
    x = X(50) 
    watcher(x, 'foo', log_file='log.txt', include =['C:/Users/j/PycharmProjects/hello']) 
    x.foo = 500 
    x.goo = 300 
    y = Y(x) 
    y.boom() 
    arr = np.arange(0,100,0.1) 
    arr = arr**2 
    for i in xrange(3): 
     print 'a' 
     x.foo = i 

    for i in xrange(1): 
     i = i+1 

main() 
+0

हां, यह बहुत धीमा है, लेकिन मैन्युअल पीडीबी से अभी भी तेज़ है, धन्यवाद। – Bunyk

+0

हाँ, तय है। वैसे, यदि आप वास्तविक की बजाय अगली पंक्ति के साथ ठीक हैं, तो इसे अधिक तेज़ तरीके से किया जा सकता है, इसे जांचें: https://gist.github.com/4086770 यह या तो अगली पंक्ति या दिखाता है वास्तविक एक, 'लाइन' घटना 'लाइन' घटना –

1

आप python debugger module (मानक पुस्तकालय का हिस्सा) का उपयोग कर सकते

उपयोग करने के लिए, बस अपने स्रोत फ़ाइल के शीर्ष पर pdb आयात:

import pdb 

और फिर एक का पता लगाने के लिए सेट भी आप करना चाहते हैं

pdb.set_trace() 

तो आप n साथ कोड के माध्यम से कदम कर सकते हैं और कॉम अजगर चलाकर वर्तमान स्थिति की जांच: कोड का निरीक्षण शुरू मांगें।

+1

क्षमा करें, मैं जोड़ना भूल गया, मैं चाहता हूं कि यह स्वचालित रूप से काम करे। तो मैं डीबगर शुरू करता हूं, इसे अपनी हालत देता हूं, उदाहरण के लिए टाइप करें (some.module.SomeClass.my_attribute) == str), और पहली पंक्ति ढूंढें जहां स्थिति पूरी नहीं होती है। और कोड की लाखों लाइनें हैं, और मुझे नहीं पता कि चर कहाँ बदल गया है। – Bunyk

1

__setattr__ का उपयोग करें। Documentation__setattr__

+0

का पालन करने के आधार पर, यहां ध्यान देने योग्य एक बात है - यह केवल कक्षा के उदाहरणों के गुणों के साथ काम करता है जो '__setattr__' को परिभाषित करता है। कक्षा विशेषताओं के साथ इसका उपयोग करने के लिए हमें कक्षा के लिए मेटाक्लास को फिर से परिभाषित करने की आवश्यकता है, और कौन जानता है कि हमें मॉड्यूल में परिभाषित चर के साथ काम करने के लिए हमें क्या जादू चाहिए। – Bunyk

+0

बस एक सुझाव। –