 7d79e52366
			
		
	
	7d79e52366
	
	
	
		
			
			Use the debtcollector library provided variants of these functions and remove our versions to avoid more duplications. Change-Id: I526789f44899afd284803b127bec5bb349d1aa2b
		
			
				
	
	
		
			148 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| #    Copyright (C) 2014 Yahoo! Inc. All Rights Reserved.
 | |
| #
 | |
| #    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | |
| #    not use this file except in compliance with the License. You may obtain
 | |
| #    a copy of the License at
 | |
| #
 | |
| #         http://www.apache.org/licenses/LICENSE-2.0
 | |
| #
 | |
| #    Unless required by applicable law or agreed to in writing, software
 | |
| #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | |
| #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | |
| #    License for the specific language governing permissions and limitations
 | |
| #    under the License.
 | |
| 
 | |
| import warnings
 | |
| 
 | |
| from oslo_utils import reflection
 | |
| 
 | |
| 
 | |
| def deprecation(message, stacklevel=None):
 | |
|     """Warns about some type of deprecation that has been (or will be) made.
 | |
| 
 | |
|     This helper function makes it easier to interact with the warnings module
 | |
|     by standardizing the arguments that the warning function recieves so that
 | |
|     it is easier to use.
 | |
| 
 | |
|     This should be used to emit warnings to users (users can easily turn these
 | |
|     warnings off/on, see https://docs.python.org/2/library/warnings.html
 | |
|     as they see fit so that the messages do not fill up the users logs with
 | |
|     warnings that they do not wish to see in production) about functions,
 | |
|     methods, attributes or other code that is deprecated and will be removed
 | |
|     in a future release (this is done using these warnings to avoid breaking
 | |
|     existing users of those functions, methods, code; which a library should
 | |
|     avoid doing by always giving at *least* N + 1 release for users to address
 | |
|     the deprecation warnings).
 | |
|     """
 | |
|     if stacklevel is None:
 | |
|         warnings.warn(message, category=DeprecationWarning)
 | |
|     else:
 | |
|         warnings.warn(message,
 | |
|                       category=DeprecationWarning, stacklevel=stacklevel)
 | |
| 
 | |
| 
 | |
| # Helper accessors for the moved proxy (since it will not have easy access
 | |
| # to its own getattr and setattr functions).
 | |
| _setattr = object.__setattr__
 | |
| _getattr = object.__getattribute__
 | |
| 
 | |
| 
 | |
| class MovedClassProxy(object):
 | |
|     """Acts as a proxy to a class that was moved to another location.
 | |
| 
 | |
|     Partially based on:
 | |
| 
 | |
|     http://code.activestate.com/recipes/496741-object-proxying/ and other
 | |
|     various examination of how to make a good enough proxy for our usage to
 | |
|     move the various types we want to move during the deprecation process.
 | |
| 
 | |
|     And partially based on the wrapt object proxy (which we should just use
 | |
|     when it becomes available @ http://review.openstack.org/#/c/94754/).
 | |
|     """
 | |
| 
 | |
|     __slots__ = [
 | |
|         '__wrapped__', '__message__', '__stacklevel__',
 | |
|         # Ensure weakrefs can be made,
 | |
|         # https://docs.python.org/2/reference/datamodel.html#slots
 | |
|         '__weakref__',
 | |
|     ]
 | |
| 
 | |
|     def __init__(self, wrapped, message, stacklevel):
 | |
|         # We can't assign to these directly, since we are overriding getattr
 | |
|         # and setattr and delattr so we have to do this hoop jump to ensure
 | |
|         # that we don't invoke those methods (and cause infinite recursion).
 | |
|         _setattr(self, '__wrapped__', wrapped)
 | |
|         _setattr(self, '__message__', message)
 | |
|         _setattr(self, '__stacklevel__', stacklevel)
 | |
|         try:
 | |
|             _setattr(self, '__qualname__', wrapped.__qualname__)
 | |
|         except AttributeError:
 | |
|             pass
 | |
| 
 | |
|     def __instancecheck__(self, instance):
 | |
|         deprecation(_getattr(self, '__message__'),
 | |
|                     stacklevel=_getattr(self, '__stacklevel__'))
 | |
|         return isinstance(instance, _getattr(self, '__wrapped__'))
 | |
| 
 | |
|     def __subclasscheck__(self, instance):
 | |
|         deprecation(_getattr(self, '__message__'),
 | |
|                     stacklevel=_getattr(self, '__stacklevel__'))
 | |
|         return issubclass(instance, _getattr(self, '__wrapped__'))
 | |
| 
 | |
|     def __call__(self, *args, **kwargs):
 | |
|         deprecation(_getattr(self, '__message__'),
 | |
|                     stacklevel=_getattr(self, '__stacklevel__'))
 | |
|         return _getattr(self, '__wrapped__')(*args, **kwargs)
 | |
| 
 | |
|     def __getattribute__(self, name):
 | |
|         return getattr(_getattr(self, '__wrapped__'), name)
 | |
| 
 | |
|     def __setattr__(self, name, value):
 | |
|         setattr(_getattr(self, '__wrapped__'), name, value)
 | |
| 
 | |
|     def __delattr__(self, name):
 | |
|         delattr(_getattr(self, '__wrapped__'), name)
 | |
| 
 | |
|     def __repr__(self):
 | |
|         wrapped = _getattr(self, '__wrapped__')
 | |
|         return "<%s at 0x%x for %r at 0x%x>" % (
 | |
|             type(self).__name__, id(self), wrapped, id(wrapped))
 | |
| 
 | |
| 
 | |
| def _generate_message(prefix, postfix=None, message=None,
 | |
|                       version=None, removal_version=None):
 | |
|     message_components = [prefix]
 | |
|     if version:
 | |
|         message_components.append(" in version '%s'" % version)
 | |
|     if removal_version:
 | |
|         if removal_version == "?":
 | |
|             message_components.append(" and will be removed in a future"
 | |
|                                       " version")
 | |
|         else:
 | |
|             message_components.append(" and will be removed in version '%s'"
 | |
|                                       % removal_version)
 | |
|     if postfix:
 | |
|         message_components.append(postfix)
 | |
|     if message:
 | |
|         message_components.append(": %s" % message)
 | |
|     return ''.join(message_components)
 | |
| 
 | |
| 
 | |
| def moved_proxy_class(new_class, old_class_name, old_module_name,
 | |
|                       message=None, version=None, removal_version=None,
 | |
|                       stacklevel=3):
 | |
|     """Deprecates a class that was moved to another location.
 | |
| 
 | |
|     This will emit warnings when the old locations class is initialized,
 | |
|     telling where the new and improved location for the old class now is.
 | |
|     """
 | |
|     old_name = ".".join((old_module_name, old_class_name))
 | |
|     new_name = reflection.get_class_name(new_class)
 | |
|     prefix = "Class '%s' has moved to '%s'" % (old_name, new_name)
 | |
|     out_message = _generate_message(prefix,
 | |
|                                     message=message, version=version,
 | |
|                                     removal_version=removal_version)
 | |
|     return MovedClassProxy(new_class, out_message, stacklevel=stacklevel)
 |