How to warn about class (name) deprecation

后端 未结 7 1465
滥情空心
滥情空心 2020-12-13 12:21

I have renamed a python class that is part of a library. I am willing to leave a possibility to use its previous name for some time but would like to warn user that it\'s de

7条回答
  •  天涯浪人
    2020-12-13 12:51

    Use inspect module to add placeholder for OldClass, then OldClsName is NewClsName check will pass, and a linter like pylint will inform this as error.

    deprecate.py

    import inspect
    import warnings
    from functools import wraps
    
    def renamed(old_name):
        """Return decorator for renamed callable.
    
        Args:
            old_name (str): This name will still accessible,
                but call it will result a warn.
    
        Returns:
            decorator: this will do the setting about `old_name`
                in the caller's module namespace.
        """
    
        def _wrap(obj):
            assert callable(obj)
    
            def _warn():
                warnings.warn('Renamed: {} -> {}'
                            .format(old_name, obj.__name__),
                            DeprecationWarning, stacklevel=3)
    
            def _wrap_with_warn(func, is_inspect):
                @wraps(func)
                def _func(*args, **kwargs):
                    if is_inspect:
                        # XXX: If use another name to call,
                        # you will not get the warning.
                        frame = inspect.currentframe().f_back
                        code = inspect.getframeinfo(frame).code_context
                        if [line for line in code
                                if old_name in line]:
                            _warn()
                    else:
                        _warn()
                    return func(*args, **kwargs)
                return _func
    
            # Make old name available.
            frame = inspect.currentframe().f_back
            assert old_name not in frame.f_globals, (
                'Name already in use.', old_name)
    
            if inspect.isclass(obj):
                obj.__init__ = _wrap_with_warn(obj.__init__, True)
                placeholder = obj
            else:
                placeholder = _wrap_with_warn(obj, False)
    
            frame.f_globals[old_name] = placeholder
    
            return obj
    
        return _wrap
    

    test.py

    from __future__ import print_function
    
    from deprecate import renamed
    
    
    @renamed('test1_old')
    def test1():
        return 'test1'
    
    
    @renamed('Test2_old')
    class Test2(object):
        pass
    
        def __init__(self):
            self.data = 'test2_data'
    
        def method(self):
            return self.data
    
    # pylint: disable=undefined-variable
    # If not use this inline pylint option, 
    # there will be E0602 for each old name.
    assert(test1() == test1_old())
    assert(Test2_old is Test2)
    print('# Call new name')
    print(Test2())
    print('# Call old name')
    print(Test2_old())
    

    then run python -W all test.py:

    test.py:22: DeprecationWarning: Renamed: test1_old -> test1
    # Call new name
    <__main__.Test2 object at 0x0000000007A147B8>
    # Call old name
    test.py:27: DeprecationWarning: Renamed: Test2_old -> Test2
    <__main__.Test2 object at 0x0000000007A147B8>
    

提交回复
热议问题