파이썬 표준 lib의 데코레이터 (@deprecated 구체적으로)


127

루틴을 deprecated로 표시해야하지만 deprecation에 대한 표준 라이브러리 데코레이터가없는 것 같습니다. 나는 그것과 경고 모듈에 대한 레시피를 알고 있지만 내 질문은 :이 (일반적인) 작업에 대한 표준 라이브러리 데코레이터가없는 이유는 무엇입니까?

추가 질문 : 표준 라이브러리에 표준 데코레이터가 전혀 있습니까?


13
이제 deprecation 패키지가 있습니다
muon

11
나는 그것을하는 방법을 이해하지만 그것이 std lib에없는 이유에 대한 통찰력을 얻기 위해 여기에 왔으며 (OP의 경우라고 가정) 실제 질문에 대한 좋은 대답을 보지 못했습니다
SwimBikeRun

4
질문에 답을 시도하지 않는 수십 개의 답변을 얻고 "나는 조리법을 알고 있습니다"와 같은 것을 적극적으로 무시하는 이유는 무엇입니까? 미친 짓이야!
Catskul

1
가짜 인터넷 포인트 때문에 @Catskul.
Stefano Borini

1
더 이상 사용 되지 않는 라이브러리를 사용할 수 있습니다 .
Laurent LAPORTE

답변:


59

다음은 Leandro가 인용 한 내용을 수정 한 일부 스 니펫입니다.

import warnings
import functools

def deprecated(func):
    """This is a decorator which can be used to mark functions
    as deprecated. It will result in a warning being emitted
    when the function is used."""
    @functools.wraps(func)
    def new_func(*args, **kwargs):
        warnings.simplefilter('always', DeprecationWarning)  # turn off filter
        warnings.warn("Call to deprecated function {}.".format(func.__name__),
                      category=DeprecationWarning,
                      stacklevel=2)
        warnings.simplefilter('default', DeprecationWarning)  # reset filter
        return func(*args, **kwargs)
    return new_func

# Examples

@deprecated
def some_old_function(x, y):
    return x + y

class SomeClass:
    @deprecated
    def some_old_method(self, x, y):
        return x + y

일부 인터프리터에서 첫 번째 솔루션이 노출되면 (필터 처리없이) 경고가 표시되지 않을 수 있습니다.


14
functools.wraps이름과 문서를 그렇게 설정하는 것보다 사용 하지 않겠습니까?
Maximilian

1
@Maximilian : 편집 잘못도 그 일에서이 코드의 미래 복사 pasters을 저장, 그 추가
에릭

17
나는 부작용 (필터 켜기 / 끄기)을 좋아하지 않습니다. 이것을 결정하는 것은 데코레이터의 일이 아닙니다.
Kentzo 2017

1
에 5 월 트리거 오프 필터 켜기 bugs.python.org/issue29672
리트

4
실제 질문에 대답하지 않습니다.
Catskul

44

다음은 또 다른 해결책입니다.

이 데코레이터 ( 사실 데코레이터 팩토리 )를 사용하면 이유 메시지 를 제공 할 수 있습니다 . 또한 소스 파일 이름줄 번호 를 제공하여 개발자가 문제를 진단하는 데 도움이 됩니다.

편집 :이 코드는 Zero의 권장 사항을 사용 합니다. 함수 정의 사이트가 아닌 함수 호출 사이트를 인쇄하는 warnings.warn_explicit행을 warnings.warn(msg, category=DeprecationWarning, stacklevel=2)로 바꿉니다. 디버깅이 더 쉬워집니다.

EDIT2 :이 버전은 개발자가 선택적 "이유"메시지를 지정할 수 있도록합니다.

import functools
import inspect
import warnings

string_types = (type(b''), type(u''))


def deprecated(reason):
    """
    This is a decorator which can be used to mark functions
    as deprecated. It will result in a warning being emitted
    when the function is used.
    """

    if isinstance(reason, string_types):

        # The @deprecated is used with a 'reason'.
        #
        # .. code-block:: python
        #
        #    @deprecated("please, use another function")
        #    def old_function(x, y):
        #      pass

        def decorator(func1):

            if inspect.isclass(func1):
                fmt1 = "Call to deprecated class {name} ({reason})."
            else:
                fmt1 = "Call to deprecated function {name} ({reason})."

            @functools.wraps(func1)
            def new_func1(*args, **kwargs):
                warnings.simplefilter('always', DeprecationWarning)
                warnings.warn(
                    fmt1.format(name=func1.__name__, reason=reason),
                    category=DeprecationWarning,
                    stacklevel=2
                )
                warnings.simplefilter('default', DeprecationWarning)
                return func1(*args, **kwargs)

            return new_func1

        return decorator

    elif inspect.isclass(reason) or inspect.isfunction(reason):

        # The @deprecated is used without any 'reason'.
        #
        # .. code-block:: python
        #
        #    @deprecated
        #    def old_function(x, y):
        #      pass

        func2 = reason

        if inspect.isclass(func2):
            fmt2 = "Call to deprecated class {name}."
        else:
            fmt2 = "Call to deprecated function {name}."

        @functools.wraps(func2)
        def new_func2(*args, **kwargs):
            warnings.simplefilter('always', DeprecationWarning)
            warnings.warn(
                fmt2.format(name=func2.__name__),
                category=DeprecationWarning,
                stacklevel=2
            )
            warnings.simplefilter('default', DeprecationWarning)
            return func2(*args, **kwargs)

        return new_func2

    else:
        raise TypeError(repr(type(reason)))

이 데코레이터를 함수 , 메서드클래스에 사용할 수 있습니다 .

다음은 간단한 예입니다.

@deprecated("use another function")
def some_old_function(x, y):
    return x + y


class SomeClass(object):
    @deprecated("use another method")
    def some_old_method(self, x, y):
        return x + y


@deprecated("use another class")
class SomeOldClass(object):
    pass


some_old_function(5, 3)
SomeClass().some_old_method(8, 9)
SomeOldClass()

당신은 얻을 것이다:

deprecated_example.py:59: DeprecationWarning: Call to deprecated function or method some_old_function (use another function).
  some_old_function(5, 3)
deprecated_example.py:60: DeprecationWarning: Call to deprecated function or method some_old_method (use another method).
  SomeClass().some_old_method(8, 9)
deprecated_example.py:61: DeprecationWarning: Call to deprecated class SomeOldClass (use another class).
  SomeOldClass()

EDIT3 : 이 데코레이터는 이제 Deprecated 라이브러리의 일부입니다.

새로운 안정 릴리스 v1.2.10 🎉


6
잘 작동합니다- 함수 정의 사이트보다는 함수 호출 사이트를 인쇄하는 warn_explicit줄을 바꾸는 것을 선호 warnings.warn(msg, category=DeprecationWarning, stacklevel=2)합니다. 디버깅이 더 쉬워집니다.
Zero

안녕하세요, GPLv3 라이선스 라이브러리 에서 코드 스 니펫을 사용하고 싶습니다 . 내가 합법적으로 그렇게 할 수 있도록 GPLv3 또는 더 많은 허용 라이센스에 따라 코드를 재 라이선스 하시겠습니까?
gerrit


1
@LaurentLAPORTE 알아요. CC-BY-SO는 GPLv3 내에서 사용하는 것을 허용하지 않습니다 (유사 유사 비트로 인해). 이것이 GPL 호환 라이선스에 따라이 코드를 추가로 릴리스 할 의향이 있는지 묻는 이유입니다. 그렇지 않다면 괜찮습니다. 귀하의 코드를 사용하지 않을 것입니다.
gerrit

2
실제 질문에 대답하지 않습니다.
Catskul

15

muon이 제안했듯이deprecation 이를위한 패키지를 설치할 수 있습니다 .

deprecation라이브러리는 제공 deprecated장식과 fail_if_not_removed당신의 테스트를위한 장식한다.

설치

pip install deprecation

사용 예

import deprecation

@deprecation.deprecated(deprecated_in="1.0", removed_in="2.0",
                        current_version=__version__,
                        details="Use the bar function instead")
def foo():
    """Do some stuff"""
    return 1

전체 문서는 http://deprecation.readthedocs.io/ 를 참조 하십시오 .


4
실제 질문에 대답하지 않습니다.
Catskul

1
참고 PyCharm 은 이것을 인식하지 못합니다
cz

11

그 이유는 파이썬 코드를 정적으로 처리 할 수 ​​없기 때문이라고 생각합니다 (C ++ 컴파일러의 경우처럼), 실제로 사용하기 전에 몇 가지 사용에 대한 경고를받을 수 없습니다. "경고 :이 스크립트 개발자는 더 이상 사용되지 않는 API를 사용하고 있습니다."라는 메시지로 스크립트 사용자에게 스팸을 보내는 것은 좋지 않다고 생각합니다.

업데이트 : 하지만 원래 기능을 다른 기능으로 변환하는 데코레이터를 만들 수 있습니다. 새로운 기능은이 기능이 이미 호출되었음을 알리는 스위치를 표시 / 확인하고 스위치를 on 상태로 전환 할 때만 메시지를 표시합니다. 그리고 / 또는 종료시 프로그램에서 사용 된 모든 비추천 기능의 목록을 인쇄 할 수 있습니다.


3
그리고 모듈에서 함수를 가져올 때 사용 중단을 표시 할 수 있어야 합니다 . 데코레이터는이를위한 올바른 도구가 될 것입니다.
Janusz Lenar 2013

@JanuszLenar, 우리가 더 이상 사용되지 않는 기능을 사용하지 않더라도 경고가 표시됩니다. 그러나 나는 약간의 힌트로 내 대답을 업데이트 할 수 있다고 생각합니다.
ony

8

utils 파일을 만들 수 있습니다.

import warnings

def deprecated(message):
  def deprecated_decorator(func):
      def deprecated_func(*args, **kwargs):
          warnings.warn("{} is a deprecated function. {}".format(func.__name__, message),
                        category=DeprecationWarning,
                        stacklevel=2)
          warnings.simplefilter('default', DeprecationWarning)
          return func(*args, **kwargs)
      return deprecated_func
  return deprecated_decorator

그리고 다음과 같이 지원 중단 데코레이터를 가져옵니다.

from .utils import deprecated

@deprecated("Use method yyy instead")
def some_method()"
 pass

감사합니다.이 기능을 사용하여 지원 중단 메시지를 표시하는 대신 올바른 위치로 사용자를 보냅니다.
German Attanasio

3
실제 질문에 대답하지 않습니다.
Catskul

2

업데이트 : 각 코드 줄대해 DeprecationWarning을 처음 표시 하고 일부 메시지를 보낼 수있을 때 더 좋습니다 .

import inspect
import traceback
import warnings
import functools

import time


def deprecated(message: str = ''):
    """
    This is a decorator which can be used to mark functions
    as deprecated. It will result in a warning being emitted
    when the function is used first time and filter is set for show DeprecationWarning.
    """
    def decorator_wrapper(func):
        @functools.wraps(func)
        def function_wrapper(*args, **kwargs):
            current_call_source = '|'.join(traceback.format_stack(inspect.currentframe()))
            if current_call_source not in function_wrapper.last_call_source:
                warnings.warn("Function {} is now deprecated! {}".format(func.__name__, message),
                              category=DeprecationWarning, stacklevel=2)
                function_wrapper.last_call_source.add(current_call_source)

            return func(*args, **kwargs)

        function_wrapper.last_call_source = set()

        return function_wrapper
    return decorator_wrapper


@deprecated('You must use my_func2!')
def my_func():
    time.sleep(.1)
    print('aaa')
    time.sleep(.1)


def my_func2():
    print('bbb')


warnings.simplefilter('always', DeprecationWarning)  # turn off filter
print('before cycle')
for i in range(5):
    my_func()
print('after cycle')
my_func()
my_func()
my_func()

결과:

before cycle
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:45: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
aaa
aaa
aaa
aaa
after cycle
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:47: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:48: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:49: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa

Process finished with exit code 0

경고 경로를 클릭하고 PyCharm의 줄로 이동할 수 있습니다.


2
실제 질문에 대답하지 않습니다.
Catskul

0

보강 스티븐 Vascellaro로이 답변 :

Anaconda를 사용하는 경우 먼저 deprecation패키지를 설치하십시오 .

conda install -c conda-forge deprecation 

그런 다음 파일 상단에 다음을 붙여 넣습니다.

import deprecation

@deprecation.deprecated(deprecated_in="1.0", removed_in="2.0",
                    current_version=__version__,
                    details="Use the bar function instead")
def foo():
    """Do some stuff"""
    return 1

전체 문서는 http://deprecation.readthedocs.io/ 를 참조 하십시오 .


4
실제 질문에 대답하지 않습니다.
Catskul
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.