2011-02-15 44 views
1

我有一個Python裝飾器,我用它來標記函數/方法爲廢棄。它工作足夠,但我想它的工作更好。具體來說,我想要它做的是能夠告訴確切的行號棄用函數。這樣就不需要通過源代碼來查找它;相反,警告將直接指向它。 (這並不是說有人不會通過代碼grep來尋找其他地方使用調用已棄用的函數,但他們不應該這樣做,以響應這樣的警告,恕我直言)。獲取有關在Python裝飾器中調用代碼的具體信息

class deprecated(object): 
    '''This class implements the deprecated decorator. 

    This decorator is used to mark a function or a class as 
    deprecated. A message indicating why it is deprecated (and what 
    to use instead) must be provided. Also, a version number 
    indicating when the function or class was deprecated must _also_ 
    be provided. 

    Optionally, a third parameter may be passed that indicates when 
    the deprecated functionality will actually be removed.''' 
    def __init__(self, message, version, to_be_removed_in_version = None): 
     self._message = message 
     self._version = version 
     self._removal_version = to_be_removed_in_version 
     return 

    def __call__(self, func): 
     def wrapper(*args, **kwargs): 
      import warnings 
      import inspect 

      frame = inspect.currentframe().f_back 
      where = 'Called from {0} somewhere after line {1}.' 
      where = where.format(frame.f_code.co_filename, 
           frame.f_code.co_firstlineno) 
      msg = 'Call to deprecated function {0} (since {1}{2}) {3}' 

      if self._removal_version is not None: 
       removal = ', scheduled for removal by version {0}' 
       removal = removal.format(self._removal_version) 

      msg = msg.format(func.__name__, self._version, 
          removal, where) 
      warnings.warn_explicit(msg, category = DeprecationWarning, 
            filename = func.func_code.co_filename, 
            lineno = func.func_code.co_firstlineno + 1) 

      return func(*args, **kwargs) 

     wrapper.__name__ = func.__name__ 
     wrapper.__doc__ = func.__doc__ 
     wrapper.__dict__.update(func.__dict__) 
     return wrapper 
+1

co_firstlineno現在給你什麼?你可以使用模塊檢查來增加堆棧到函數被調用的地方嗎?一旦你到了正確的框架,f_lineno應該告訴你你想要什麼 - 雖然我從來沒有用過裝飾者之前... – 2011-02-15 01:41:33

回答

9

你並不需要所有的東西:

from functools import wraps 
import warnings 

def deprecated(message, newfunc): 
    def _deprecated(func): 
     @wraps(func) 
     def _wrapped(*args, **kwds): 
      warnings.warn(message + ', ' + newfunc, 
       DeprecationWarning, stacklevel=2) 
      return func(*args, **kwds) 
     return _wrapped 
    return _deprecated 

這一切!檢查出來:

@deprecated("function foo is deprecated", "use bar instead") 
def foo(bar): 
    print 'Hello', bar 
foo('World') 

我得到

teste.py:17: DeprecationWarning: function foo is deprecated, use bar instead 
    foo('World') 
Hello World 

又如:

class Foo(object): 
    @deprecated("method bar is deprecated", "use baz instead") 
    def bar(self, baz): 
     print 'Goodbye', baz 
f = Foo() 
f.bar('World') 

我得到:

teste.py:25: DeprecationWarning: method bar is deprecated, use baz instead 
    f.bar('World') 
Goodbye World 

在S ecret是stacklevel參數warnings.warn。該docs說:

的stacklevel參數可以用Python編寫的包裝功能可以使用,(...),使得警告參考折舊()的調用者,而不是棄用源()本身(因爲後者會擊敗警告信息的目的)。

+0

太棒了!謝謝! (對不起,我沒有看到這個更快,我忘了檢查電子郵件箱,我結束了不得不做一大堆其他的東西,所以我沒有在這裏一會兒...) – 2011-03-05 18:30:01