Initial import of renames/moves + tests
This adds the initial import (and adjustments to requirements and code) that was initially targeted to land into oslo.utils but now lands in this project from the following: https://review.openstack.org/#/c/140119 This forms the basis of the debtcollector functionality (with more to come as/when needed). Change-Id: Icd62622a728525fab48ba4de7ee746d0add73b9b
This commit is contained in:
parent
493c200a05
commit
b833207aaa
61
debtcollector/_utils.py
Normal file
61
debtcollector/_utils.py
Normal file
@ -0,0 +1,61 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2015 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
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def generate_message(prefix, postfix=None, message=None,
|
||||
version=None, removal_version=None):
|
||||
"""Helper to generate a common message 'style' for deprecation helpers."""
|
||||
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)
|
104
debtcollector/moves.py
Normal file
104
debtcollector/moves.py
Normal file
@ -0,0 +1,104 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2015 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 functools
|
||||
|
||||
from oslo_utils import reflection
|
||||
import six
|
||||
|
||||
from debtcollector import _utils
|
||||
|
||||
_KIND_MOVED_PREFIX_TPL = "%s '%s' has moved to '%s'"
|
||||
_CLASS_MOVED_PREFIX_TPL = "Class '%s' has moved to '%s'"
|
||||
|
||||
|
||||
def _moved_decorator(kind, new_attribute_name, message=None,
|
||||
version=None, removal_version=None, stacklevel=3):
|
||||
"""Decorates a method/property that was moved to another location."""
|
||||
|
||||
def decorator(f):
|
||||
try:
|
||||
# Prefer the py3.x name (if we can get at it...)
|
||||
old_attribute_name = f.__qualname__
|
||||
fully_qualified = True
|
||||
except AttributeError:
|
||||
old_attribute_name = f.__name__
|
||||
fully_qualified = False
|
||||
|
||||
@six.wraps(f)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
base_name = reflection.get_class_name(self, fully_qualified=False)
|
||||
if fully_qualified:
|
||||
old_name = old_attribute_name
|
||||
else:
|
||||
old_name = ".".join((base_name, old_attribute_name))
|
||||
new_name = ".".join((base_name, new_attribute_name))
|
||||
prefix = _KIND_MOVED_PREFIX_TPL % (kind, old_name, new_name)
|
||||
out_message = _utils.generate_message(
|
||||
prefix, message=message,
|
||||
version=version, removal_version=removal_version)
|
||||
_utils.deprecation(out_message, stacklevel=stacklevel)
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def moved_property(new_attribute_name, message=None,
|
||||
version=None, removal_version=None, stacklevel=3):
|
||||
"""Decorates a *instance* property that was moved to another location."""
|
||||
|
||||
return _moved_decorator('Property', new_attribute_name, message=message,
|
||||
version=version, removal_version=removal_version,
|
||||
stacklevel=stacklevel)
|
||||
|
||||
|
||||
def moved_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 creates a 'new-old' type that can be used for a
|
||||
deprecation period that can be inherited from. 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_MOVED_PREFIX_TPL % (old_name, new_name)
|
||||
out_message = _utils.generate_message(
|
||||
prefix, message=message, version=version,
|
||||
removal_version=removal_version)
|
||||
|
||||
def decorator(f):
|
||||
|
||||
# Use the older functools until the following is available:
|
||||
#
|
||||
# https://bitbucket.org/gutworth/six/issue/105
|
||||
|
||||
@functools.wraps(f, assigned=("__name__", "__doc__"))
|
||||
def wrapper(self, *args, **kwargs):
|
||||
_utils.deprecation(out_message, stacklevel=stacklevel)
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
old_class = type(old_class_name, (new_class,), {})
|
||||
old_class.__module__ = old_module_name
|
||||
old_class.__init__ = decorator(old_class.__init__)
|
||||
return old_class
|
45
debtcollector/renames.py
Normal file
45
debtcollector/renames.py
Normal file
@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2015 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 six
|
||||
|
||||
from debtcollector import _utils
|
||||
|
||||
_KWARG_RENAMED_POSTFIX_TPL = ", please use the '%s' argument instead"
|
||||
_KWARG_RENAMED_PREFIX_TPL = "Using the '%s' argument is deprecated"
|
||||
|
||||
|
||||
def renamed_kwarg(old_name, new_name, message=None,
|
||||
version=None, removal_version=None, stacklevel=3):
|
||||
"""Decorates a kwarg accepting function to deprecate a renamed kwarg."""
|
||||
|
||||
prefix = _KWARG_RENAMED_PREFIX_TPL % old_name
|
||||
postfix = _KWARG_RENAMED_POSTFIX_TPL % new_name
|
||||
out_message = _utils.generate_message(
|
||||
prefix, postfix=postfix, message=message, version=version,
|
||||
removal_version=removal_version)
|
||||
|
||||
def decorator(f):
|
||||
|
||||
@six.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
if old_name in kwargs:
|
||||
_utils.deprecation(out_message, stacklevel=stacklevel)
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
test_debtcollector
|
||||
----------------------------------
|
||||
|
||||
Tests for `debtcollector` module.
|
||||
"""
|
||||
|
||||
from debtcollector.tests import base
|
||||
|
||||
|
||||
class TestDebtcollector(base.TestCase):
|
||||
|
||||
def test_something(self):
|
||||
pass
|
116
debtcollector/tests/test_deprecation.py
Normal file
116
debtcollector/tests/test_deprecation.py
Normal file
@ -0,0 +1,116 @@
|
||||
# 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 debtcollector import moves
|
||||
from debtcollector import renames
|
||||
from debtcollector.tests import base as test_base
|
||||
|
||||
|
||||
@renames.renamed_kwarg('blip', 'blop')
|
||||
def blip_blop(blip=1, blop=1):
|
||||
return (blip, blop)
|
||||
|
||||
|
||||
class WoofWoof(object):
|
||||
@property
|
||||
def bark(self):
|
||||
return 'woof'
|
||||
|
||||
@property
|
||||
@moves.moved_property('bark')
|
||||
def burk(self):
|
||||
return self.bark
|
||||
|
||||
|
||||
class NewHotness(object):
|
||||
def hot(self):
|
||||
return 'cold'
|
||||
|
||||
|
||||
OldHotness = moves.moved_class(NewHotness, 'OldHotness', __name__)
|
||||
|
||||
|
||||
class MovedInheritableClassTest(test_base.TestCase):
|
||||
def test_basics(self):
|
||||
old = OldHotness()
|
||||
self.assertIsInstance(old, NewHotness)
|
||||
self.assertEqual('cold', old.hot())
|
||||
|
||||
def test_warnings_emitted_creation(self):
|
||||
with warnings.catch_warnings(record=True) as capture:
|
||||
warnings.simplefilter("always")
|
||||
OldHotness()
|
||||
self.assertEqual(1, len(capture))
|
||||
w = capture[0]
|
||||
self.assertEqual(DeprecationWarning, w.category)
|
||||
|
||||
def test_existing_refer_subclass(self):
|
||||
|
||||
class MyOldThing(OldHotness):
|
||||
pass
|
||||
|
||||
with warnings.catch_warnings(record=True) as capture:
|
||||
warnings.simplefilter("always")
|
||||
MyOldThing()
|
||||
|
||||
self.assertEqual(1, len(capture))
|
||||
w = capture[0]
|
||||
self.assertEqual(DeprecationWarning, w.category)
|
||||
|
||||
|
||||
class MovedPropertyTest(test_base.TestCase):
|
||||
def test_basics(self):
|
||||
dog = WoofWoof()
|
||||
self.assertEqual('woof', dog.burk)
|
||||
self.assertEqual('woof', dog.bark)
|
||||
|
||||
def test_warnings_emitted(self):
|
||||
dog = WoofWoof()
|
||||
with warnings.catch_warnings(record=True) as capture:
|
||||
warnings.simplefilter("always")
|
||||
self.assertEqual('woof', dog.burk)
|
||||
self.assertEqual(1, len(capture))
|
||||
w = capture[0]
|
||||
self.assertEqual(DeprecationWarning, w.category)
|
||||
|
||||
def test_warnings_not_emitted(self):
|
||||
dog = WoofWoof()
|
||||
with warnings.catch_warnings(record=True) as capture:
|
||||
warnings.simplefilter("always")
|
||||
self.assertEqual('woof', dog.bark)
|
||||
self.assertEqual(0, len(capture))
|
||||
|
||||
|
||||
class RenamedKwargTest(test_base.TestCase):
|
||||
def test_basics(self):
|
||||
self.assertEqual((1, 1), blip_blop())
|
||||
self.assertEqual((2, 1), blip_blop(blip=2))
|
||||
self.assertEqual((1, 2), blip_blop(blop=2))
|
||||
self.assertEqual((2, 2), blip_blop(blip=2, blop=2))
|
||||
|
||||
def test_warnings_emitted(self):
|
||||
with warnings.catch_warnings(record=True) as capture:
|
||||
warnings.simplefilter("always")
|
||||
self.assertEqual((2, 1), blip_blop(blip=2))
|
||||
self.assertEqual(1, len(capture))
|
||||
w = capture[0]
|
||||
self.assertEqual(DeprecationWarning, w.category)
|
||||
|
||||
def test_warnings_not_emitted(self):
|
||||
with warnings.catch_warnings(record=True) as capture:
|
||||
warnings.simplefilter("always")
|
||||
self.assertEqual((1, 2), blip_blop(blop=2))
|
||||
self.assertEqual(0, len(capture))
|
@ -3,4 +3,6 @@
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr>=0.6,!=0.7,<1.0
|
||||
Babel>=1.3
|
||||
Babel>=1.3
|
||||
six>=1.7.0
|
||||
oslo.utils>=1.2.0 # Apache-2.0
|
||||
|
@ -12,4 +12,4 @@ oslosphinx
|
||||
oslotest>=1.1.0.0a1
|
||||
testrepository>=0.0.18
|
||||
testscenarios>=0.4
|
||||
testtools>=0.9.34
|
||||
testtools>=0.9.34
|
||||
|
Loading…
x
Reference in New Issue
Block a user