summaryrefslogtreecommitdiff
path: root/deprecation.py
blob: f9041ec440cad3c9704749616fa2ddd3a3bbf336 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""Deprecation utilities.

:copyright: 2006-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: General Public License version 2 - http://www.gnu.org/licenses
"""
__docformat__ = "restructuredtext en"

import sys
from warnings import warn


class deprecated(type):
    """metaclass to print a warning on instantiation of a deprecated class"""
    
    def __call__(cls, *args, **kwargs):
        msg = getattr(cls, "__deprecation_warning__",
                      "%s is deprecated" % cls.__name__)
        warn(msg, DeprecationWarning, stacklevel=2)
        return type.__call__(cls, *args, **kwargs)


def class_renamed(old_name, new_class, message=None):
    """automatically creates a class which fires a DeprecationWarning
    when instantiated.
    
    >>> Set = class_renamed('Set', set, 'Set is now replaced by set')
    >>> s = Set()
    sample.py:57: DeprecationWarning: Set is now replaced by set
      s = Set()
    >>>
    """
    clsdict = {}
    if message is None:
        message = '%s is deprecated' % old_name
    clsdict['__deprecation_warning__'] = message
    try:
        # new-style class
        return deprecated(old_name, (new_class,), clsdict)
    except (NameError, TypeError):
        # old-style class
        class DeprecatedClass(new_class):
            """FIXME: There might be a better way to handle old/new-style class
            """
            def __init__(self, *args, **kwargs):
                warn(message, DeprecationWarning, stacklevel=2)
                new_class.__init__(self, *args, **kwargs)
        return DeprecatedClass


def class_moved(new_class, old_name=None, message=None):
    """nice wrapper around class_renamed when a class has been moved into
    another module
    """
    if old_name is None:
        old_name = new_class.__name__
    if message is None:
        message = 'class %s is now available as %s.%s' % (
            old_name, new_class.__module__, new_class.__name__)
    return class_renamed(old_name, new_class, message)


def deprecated_function(new_func, message=None):
    """Creates a function which fires a DeprecationWarning when used.

    For example, if <bar> is deprecated in favour of <foo>:
    
    >>> bar = deprecated_function(foo, 'bar is deprecated')
    >>> bar()
    sample.py:57: DeprecationWarning: bar is deprecated
      bar()
    >>>
    """
    if message is None:
        message = "this function is deprecated, use %s instead" % (
            new_func.__name__)
    def deprecated(*args, **kwargs):
        warn(message, DeprecationWarning, stacklevel=2)
        return new_func(*args, **kwargs)
    return deprecated


def moved(modpath, objname):
    """use to tell that a callable has been moved to a new module.

    It returns a callable wrapper, so that when its called a warning is printed
    telling where the object can be found, import is done (and not before) and
    the actual object is called.

    NOTE: the usage is somewhat limited on classes since it will fail if the
    wrapper is use in a class ancestors list, use the `class_moved` function
    instead (which has no lazy import feature though).
    """
    def callnew(*args, **kwargs):
        from logilab.common.modutils import load_module_from_name
        message = "object %s has been moved to module %s" % (objname, modpath)
        warn(message, DeprecationWarning, stacklevel=2)
        m = load_module_from_name(modpath)
        return getattr(m, objname)(*args, **kwargs)
    return callnew

# from logilab.common.modutils import LazyObject

# class WarnLazyObject(LazyObject):
#     def __init__(self, oldname, newname):
#         # XXX doesn't work if module isn't in a package
#         package, module = newname.rsplit('.', 1)
#         super(WarnLazyObject, self).__init__(package, module)
#         self.oldname = oldname
#         self.newname = newname
#         print 'hop', oldname, newname
#         sys.modules[oldname] = self

#     def __getobj(self):
#         if self._imported is None:
#             message = "module %s has moved, it's now %s" % (
#                 self.oldname, self.newname)
#             warn(message, DeprecationWarning, stacklevel=2)
#         return super(WarnLazyObject, self).__getobj()


# module_moved = WarnLazyObject

def obsolete(reason="This function is obsolete"):
    """this function is an alternative to `deprecated_function`
    when there's no real replacement for the deprecated function
    """
    def newdecorator(func):
        def wrapped(*args, **kwargs):
            warn(reason, DeprecationWarning, stacklevel=2)
            return func(*args, **kwargs)
        return wrapped
    return newdecorator