2013-10-07 7 views
10

द्वारा फ़ंक्शन निष्पादन लाइन लॉग करने के लिए सजावटी मैं एक स्क्रिप्ट पर काम कर रहा हूं जिसमें चलाने के लिए कुछ मिनट लगते हैं, और उपयोगकर्ता को इसकी प्रगति के बारे में कुछ आउटपुट प्रदान करना चाहते हैं। दुर्भाग्य से मैं बहुत आलसी हूँ। मैं जो करना चाहता हूं वह लॉगिंग के बिना एक फ़ंक्शन लिखता है, और उसके बाद एक सजावट लागू करता है जो फ़ंक्शन के माध्यम से चलता है और उस पंक्ति को निष्पादित करने से पहले प्रत्येक पंक्ति को प्रिंट करता है। मूल रूप से मैं क्या तलाश कर रहा हूँ है एक loggingdecorator ऐसी है कि:लाइन

>>> @loggingdecorator 
... def myfunction(): 
...  foo() 
...  bar() 
...  baz() 
>>> myfunction() 
Starting myfunction 
foo() ... [OK] 
bar() ... [OK] 
baz() ... [OK] 
myfunction Done! 

यहाँ क्या मैं अब तक की कोशिश की है:

import sys 


def logging_tracer(frame, event, arg): 
    def local_tracer(local_frame, event, arg): 
     if frame is local_frame: 
      print frame.f_code.co_name, event, arg 

    print frame.f_code.co_name, event, arg 
    return local_tracer 


def loggingdecorator(func): 
    def _wrapper(): 
     old_trace_function = sys.gettrace() 
     sys.settrace(logging_tracer) 
     try: 
      result = func() 
     except: 
      raise 
     else: 
      return result 
     finally: 
      sys.settrace(old_trace_function) 
    return _wrapper 

दुर्भाग्य से, यह थोड़ा बहुत ज्यादा मुद्रण करता है; यह फंक्शन कॉल का पालन करता है और लाइन को लाइन से भी प्रिंट करता है (ठीक है, यह वास्तव में स्रोत लाइन को प्रिंट नहीं करता है, निरीक्षण का उपयोग कर मौजूदा उत्तरों, ट्रेस फ़ंक्शन में फ्रेम ऑब्जेक्ट पर सामान के साथ संयुक्त किया जाएगा), लेकिन मैं logging_tracer के बारे में थोड़ा सा स्टंप किया गया है जब तक कि सवाल में वास्तव में सजाया नहीं जाता है।

+0

आप उन सभी कार्यों पर एक ही सजावटी रख सकते हैं जिन्हें आप कॉल करना चाहते हैं। फिर अपने प्रिंट स्टेटमेंट को फ़ंक्शंस के अंदर रखें। जैसे-जैसे कार्य निष्पादित होते हैं, प्रिंट स्टेटमेंट भी चलाए जाते हैं। या आप सजावट के बारे में चिंता किए बिना बस प्रत्येक फंक्शन के भीतर प्रिंट स्टेटमेंट डाल सकते हैं और इसे अभी भी – smac89

+0

@ Smac89 करना चाहिए, आपका सुझाव बहुत आक्रामक है। डीबग प्रिंट एक बहुत ही खराब ट्रेसिंग तकनीक है – J0HN

+0

निकटतम चीज़ जो मैं सोचता हूं, line_profiler होगा। आप जो भी चाहते हैं उसे करने के लिए आप शायद इसी तरह की विधि का उपयोग कर सकते हैं। दुर्भाग्यवश, line_profiler सी/साइथन में लिखा गया है, इसलिए संशोधित करना इतना आसान नहीं है। –

उत्तर

4

तो यहां मैं क्या आया हूं। @Corley Brigman की टिप्पणी ने मुझे सही दिशा में शुरू किया। यह थोड़ा हैकी है, sys.gettrace/settrace बल्कि "सीपीथन कार्यान्वयन विवरण" के रूप में प्रलेखित रूप से दस्तावेज किया गया है, और इसलिए इस समाधान को अन्य कार्यान्वयन में काम करने की उम्मीद नहीं की जानी चाहिए। ऐसा कहा जाता है, ऐसा लगता है कि यह बहुत अच्छा काम करता है। Cpython में ट्रेसिंग कार्यक्षमता "लाइन समाप्त निष्पादन" की कोई अधिसूचना प्रदान नहीं करती है, इसलिए [ok] मेरे प्रश्न से वास्तव में कोई समझ नहीं आता है।

रिकर्सिव ट्रेसिंग समस्या को ठीक करना केवल सजाए गए कार्यों के कैश को रखने का एक साधारण मामला है, और तब केवल आउटपुट का उत्पादन होता है यदि फ्रेम का पता लगाया गया है जो सजाए गए फ़ंक्शन से है।

import inspect 
import sys 


def logging_tracer(frame, event, arg): 
    lines, firstline = inspect.getsourcelines(frame) 

    def local_tracer(local_frame, event, arg): 
     if event == 'line' and frame is local_frame: 
      print event, frame.f_lineno,'\t', lines[frame.f_lineno - firstline] 
      #print event, lines[frame.f_lineno - firstline] 
      #print frame.f_code.co_name, frame.f_lineno, event, arg 

    if frame.f_code in LOG_THESE_FUNCTIONS: 
     print event, frame.f_lineno,'\t', lines[frame.f_lineno - firstline + (event == 'call')] 
     #print frame.f_code.co_name, event, arg 
     return local_tracer 
    else: 
     return None 


LOG_THESE_FUNCTIONS = set() 


def loggingdecorator(func): 
    LOG_THESE_FUNCTIONS.add(func.func_code) 

    def _wrapper(): 
     old_trace_function = sys.gettrace() 
     sys.settrace(logging_tracer) 
     try: 
      result = func() 
     except: 
      raise 
     else: 
      return result 
     finally: 
      sys.settrace(old_trace_function) 
    return _wrapper 
0

आप कुछ इस तरह कर सकते हैं:

>>> class loggingdecorator: 
...  def __init__(self, func): 
...    self.func = func 
...  def __call__(self): 
...    print "Entering", self.func.__name__ 
...    self.func() 
...    print "Exited", self.func.__name__ 
... 
फिर अपने कार्यों में से प्रत्येक के साथ

:

>>> @loggingdecorator 
... def func1(): 
...  print "Hello from func1(), how are you doing?" 
... 
>>> @loggingdecorator 
... def func2(): 
...  print "Hello from func2(), Whaddup Dawg...?" 
... 
>>> func1() 
Entering func1 
Hello from func1(), how are you doing? 
Exited func1 
>>> func2() 
Entering func2 
Hello from func2(), Whaddup Dawg...? 
Exited func2 

से this page

+0

क्षमा करें, मैं जो कुछ भी ढूंढ रहा हूं वह बिल्कुल नहीं। कल्पना कीजिए कि समारोह दस लाइन लंबी है, और प्रत्येक पंक्ति को चलाने के लिए 1 मिनट लगते हैं। मैं इसे निष्पादित करने से ठीक पहले कंसोल पर मुद्रित वास्तविक स्रोत रेखा देखना चाहता हूं, आउटपुट के बाहर दस लाइनें, प्रत्येक एक मिनट के अंतराल से अलग हो जाती हैं। – SingleNegationElimination

1

मिले उदाहरण यहाँ एक बदसूरत उदाहरण है कि काम करता है हो, तो केवल एक इंडेंटेशन स्तर:

import inspect 


def myDec(func): 
    temp = list(inspect.getsourcelines(func)[0]) 

    temp.append(' print("{} Done!")\n'.format(func.__name__)) 
    for index in range(len(temp)-2, 0, -1): 
     temp.insert(index+1, " print('''{}...[OK]''')\n".format(temp[index].strip().rstrip("\n"))) 
    temp.insert(1, ' print("Starting {}")\n'.format(func.__name__)) 
    temp = "".join(temp) 
    exec(temp) 
    return locals()[func.__name__] 

def foo(): 
    a = 4+5 
    list_int = range(100) 


foo = myDec(foo) 
foo() 

वास्तव में बदसूरत हालांकि ...