Package logilab :: Package common :: Module deprecation
[frames] | no frames]

Source Code for Module logilab.common.deprecation

  1  # copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
  2  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr 
  3  # 
  4  # This file is part of logilab-common. 
  5  # 
  6  # logilab-common is free software: you can redistribute it and/or modify it under 
  7  # the terms of the GNU Lesser General Public License as published by the Free 
  8  # Software Foundation, either version 2.1 of the License, or (at your option) any 
  9  # later version. 
 10  # 
 11  # logilab-common is distributed in the hope that it will be useful, but WITHOUT 
 12  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 13  # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 14  # details. 
 15  # 
 16  # You should have received a copy of the GNU Lesser General Public License along 
 17  # with logilab-common.  If not, see <http://www.gnu.org/licenses/>. 
 18  """Deprecation utilities.""" 
 19   
 20  __docformat__ = "restructuredtext en" 
 21   
 22  import sys 
 23  from warnings import warn 
 24   
 25  from logilab.common.changelog import Version 
 26   
 27   
28 -class DeprecationWrapper(object):
29 """proxy to print a warning on access to any attribute of the wrapped object 30 """
31 - def __init__(self, proxied, msg=None):
32 self._proxied = proxied 33 self._msg = msg
34
35 - def __getattr__(self, attr):
36 warn(self._msg, DeprecationWarning, stacklevel=2) 37 return getattr(self._proxied, attr)
38
39 - def __setattr__(self, attr, value):
40 if attr in ('_proxied', '_msg'): 41 self.__dict__[attr] = value 42 else: 43 warn(self._msg, DeprecationWarning, stacklevel=2) 44 setattr(self._proxied, attr, value)
45 46
47 -class DeprecationManager(object):
48 """Manage the deprecation message handling. Messages are dropped for 49 versions more recent than the 'compatible' version. Example:: 50 51 deprecator = deprecation.DeprecationManager("module_name") 52 deprecator.compatibility('1.3') 53 54 deprecator.warn('1.2', "message.") 55 56 @deprecator.deprecated('1.2', 'Message') 57 def any_func(): 58 pass 59 60 class AnyClass(object): 61 __metaclass__ = deprecator.class_deprecated('1.2') 62 """
63 - def __init__(self, module_name=None):
64 """ 65 """ 66 self.module_name = module_name 67 self.compatible_version = None
68
69 - def compatibility(self, compatible_version):
70 """Set the compatible version. 71 """ 72 self.compatible_version = Version(compatible_version)
73
74 - def deprecated(self, version=None, reason=None, stacklevel=2, name=None, doc=None):
75 """Display a deprecation message only if the version is older than the 76 compatible version. 77 """ 78 def decorator(func): 79 message = reason or 'The function "%s" is deprecated' 80 if '%s' in message: 81 message %= func.__name__ 82 def wrapped(*args, **kwargs): 83 self.warn(version, message, stacklevel+1) 84 return func(*args, **kwargs)
85 return wrapped
86 return decorator 87
88 - def class_deprecated(self, version=None):
89 class metaclass(type): 90 """metaclass to print a warning on instantiation of a deprecated class""" 91 92 def __call__(cls, *args, **kwargs): 93 msg = getattr(cls, "__deprecation_warning__", 94 "%(cls)s is deprecated") % {'cls': cls.__name__} 95 self.warn(version, msg, stacklevel=3) 96 return type.__call__(cls, *args, **kwargs)
97 return metaclass 98
99 - def moved(self, version, modpath, objname):
100 """use to tell that a callable has been moved to a new module. 101 102 It returns a callable wrapper, so that when its called a warning is printed 103 telling where the object can be found, import is done (and not before) and 104 the actual object is called. 105 106 NOTE: the usage is somewhat limited on classes since it will fail if the 107 wrapper is use in a class ancestors list, use the `class_moved` function 108 instead (which has no lazy import feature though). 109 """ 110 def callnew(*args, **kwargs): 111 from logilab.common.modutils import load_module_from_name 112 message = "object %s has been moved to module %s" % (objname, modpath) 113 self.warn(version, message) 114 m = load_module_from_name(modpath) 115 return getattr(m, objname)(*args, **kwargs)
116 return callnew 117
118 - def class_renamed(self, version, old_name, new_class, message=None):
119 clsdict = {} 120 if message is None: 121 message = '%s is deprecated, use %s' % (old_name, new_class.__name__) 122 clsdict['__deprecation_warning__'] = message 123 try: 124 # new-style class 125 return self.class_deprecated(version)(old_name, (new_class,), clsdict) 126 except (NameError, TypeError): 127 # old-style class 128 warn = self.warn 129 class DeprecatedClass(new_class): 130 """FIXME: There might be a better way to handle old/new-style class 131 """ 132 def __init__(self, *args, **kwargs): 133 warn(version, message, stacklevel=3) 134 new_class.__init__(self, *args, **kwargs)
135 return DeprecatedClass 136
137 - def class_moved(self, version, new_class, old_name=None, message=None):
138 """nice wrapper around class_renamed when a class has been moved into 139 another module 140 """ 141 if old_name is None: 142 old_name = new_class.__name__ 143 if message is None: 144 message = 'class %s is now available as %s.%s' % ( 145 old_name, new_class.__module__, new_class.__name__) 146 return self.class_renamed(version, old_name, new_class, message)
147
148 - def warn(self, version=None, reason="", stacklevel=2):
149 """Display a deprecation message only if the version is older than the 150 compatible version. 151 """ 152 if (self.compatible_version is None 153 or version is None 154 or Version(version) < self.compatible_version): 155 if self.module_name and version: 156 reason = '[%s %s] %s' % (self.module_name, version, reason) 157 elif self.module_name: 158 reason = '[%s] %s' % (self.module_name, reason) 159 elif version: 160 reason = '[%s] %s' % (version, reason) 161 warn(reason, DeprecationWarning, stacklevel=stacklevel)
162 163 _defaultdeprecator = DeprecationManager() 164
165 -def deprecated(reason=None, stacklevel=2, name=None, doc=None):
166 return _defaultdeprecator.deprecated(None, reason, stacklevel, name, doc)
167 168 class_deprecated = _defaultdeprecator.class_deprecated() 169
170 -def moved(modpath, objname):
171 return _defaultdeprecator.moved(None, modpath, objname)
172 moved.__doc__ = _defaultdeprecator.moved.__doc__ 173
174 -def class_renamed(old_name, new_class, message=None):
175 """automatically creates a class which fires a DeprecationWarning 176 when instantiated. 177 178 >>> Set = class_renamed('Set', set, 'Set is now replaced by set') 179 >>> s = Set() 180 sample.py:57: DeprecationWarning: Set is now replaced by set 181 s = Set() 182 >>> 183 """ 184 return _defaultdeprecator.class_renamed(None, old_name, new_class, message)
185
186 -def class_moved(new_class, old_name=None, message=None):
187 return _defaultdeprecator.class_moved(None, new_class, old_name, message)
188 class_moved.__doc__ = _defaultdeprecator.class_moved.__doc__ 189