2012-11-15 33 views
11

Có dự án python lớn trong đó một thuộc tính của một lớp chỉ có giá trị sai ở một số nơi.Quan sát sự thay đổi trong python

Nó phải là sqlalchemy.orm.attributes.InstrumentedAttribute, nhưng khi tôi chạy thử nghiệm nó là giá trị không đổi, hãy nói chuỗi.

Có một số cách để chạy chương trình python ở chế độ gỡ lỗi và chạy một số kiểm tra (nếu biến loại thay đổi) sau mỗi dòng mã thông báo tự động?

P.S. Tôi biết làm thế nào để đăng nhập thay đổi thuộc tính của thể hiện lớp với sự giúp đỡ của kiểm tra và trang trí tài sản. Có thể ở đây tôi có thể sử dụng phương pháp này với metaclasses ...

Nhưng đôi khi tôi cần tổng quát hơn và giải pháp mạnh mẽ ...

Cảm ơn bạn.

P.P.S. Tôi cần một cái gì đó như ở đó: https://stackoverflow.com/a/7669165/816449, nhưng có thể giải thích thêm về những gì đang xảy ra trong mã đó.

Trả lời

11

Vâng, đây là loại phương thức chậm. Nó có thể được sửa đổi để xem thay đổi biến cục bộ (chỉ bằng tên). Đây là cách nó hoạt động: chúng tôi làm sys.settrace và phân tích giá trị của obj.attr mỗi bước. Phần khó khăn là chúng tôi nhận được 'line' sự kiện (một số dòng đã được thực thi) trước khi thực hiện dòng. Vì vậy, khi chúng ta nhận thấy obj.attr đã thay đổi, chúng ta đã ở trên dòng tiếp theo và chúng ta không thể có được khung hình trước đó (vì khung không được sao chép cho mỗi dòng, chúng được sửa đổi). Vì vậy, trên mỗi sự kiện dòng tôi lưu traceback.format_stack đến watcher.prev_st và nếu cuộc gọi tiếp theo của trace_command giá trị đã thay đổi, chúng tôi sẽ in dấu vết ngăn xếp đã lưu vào tệp. Tiết kiệm traceback trên mỗi dòng là một hoạt động khá tốn kém, vì vậy bạn phải đặt từ khóa include thành danh sách thư mục dự án của bạn (hoặc chỉ là thư mục gốc của dự án) để không xem các thư viện khác đang làm gì và lãng phí CPU.

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

Có, điều này rất chậm, nhưng vẫn nhanh hơn pdb thủ công, cảm ơn bạn. – Bunyk

+0

vâng, đã sửa. Nhân tiện, nếu bạn đồng ý với dòng tiếp theo thay vì thực tế, nó có thể được thực hiện theo cách nhanh hơn nhiều, hãy kiểm tra điều này: https://gist.github.com/4086770 nó hiển thị dòng tiếp theo hoặc một thực tế, tùy thuộc vào sự kiện 'line' theo sự kiện' line' –

1

Bạn có thể sử dụng python debugger module (một phần của thư viện chuẩn)

Để sử dụng, chỉ cần nhập pdb ở phía trên cùng của tập tin nguồn của bạn:

import pdb 

và sau đó đặt một dấu vết bất cứ nơi nào bạn muốn bắt đầu kiểm tra mã:

pdb.set_trace() 

sau đó bạn có thể bước qua mã với n, và điều tra hiện trạng bằng cách chạy python com mands.

+1

Rất tiếc, tôi đã quên thêm, tôi muốn điều này được nghĩ là hoạt động tự động. Vì vậy, tôi bắt đầu trình gỡ lỗi, cho nó điều kiện của tôi, ví dụ loại (some.module.SomeClass.my_attribute) == str), và tìm dòng đầu tiên mà điều kiện không đáp ứng. Và có hàng triệu dòng mã và tôi không biết biến được thay đổi ở đâu. – Bunyk

1

Hãy thử sử dụng __setattr__. Documentation cho __setattr__

+0

Có một điều cần lưu ý ở đây - điều này chỉ hoạt động với các thuộc tính của các cá thể của lớp định nghĩa '__setattr__'. Để sử dụng nó với các thuộc tính lớp, chúng ta cần định nghĩa lại metaclass cho lớp, và ai biết được ma thuật nào chúng ta cần để làm cho nó hoạt động với các biến được định nghĩa trong mô-đun. – Bunyk

+0

Chỉ là một gợi ý. –

Các vấn đề liên quan