Implement Parameter Deprecation

Implements parameter deprecation for magnum client via decorator
in cliutils.py and additional logic in magnumclient/shell.py.

Logic in shell creates mutual exclusive (mutex) groups in argparse
to enable parameter checking for required and unrequired parameters
specifically to check for old parameters that are being deprecated
in favor of newer ones.

Change-Id: Ic72c876e556070a066b1693a119a389301dfa259
Implements: blueprint rename-id-parameter-names
This commit is contained in:
Stephen Watson 2016-10-18 13:55:55 -07:00
parent 06290d21d1
commit e7f084dfa9
2 changed files with 89 additions and 2 deletions

View File

@ -35,6 +35,12 @@ from six import moves
from magnumclient.i18n import _
def deprecation_message(preamble, new_name):
msg = ('%s This parameter is deprecated and will be removed in a future '
'release. Use --%s instead.' % (preamble, new_name))
return msg
class MissingArgs(Exception):
"""Supplied arguments are not sufficient for calling a function."""
def __init__(self, missing):
@ -77,14 +83,14 @@ def validate_args(fn, *args, **kwargs):
def deprecated(message):
'''Decorator for marking a call as deprecated by printing a given message.
"""Decorator for marking a call as deprecated by printing a given message.
Example:
>>> @deprecated("Bay functions are deprecated and should be replaced by "
... "calls to cluster")
... def bay_create(args):
... pass
'''
"""
@decorator.decorator
def wrapper(func, *args, **kwargs):
print(message)
@ -92,6 +98,57 @@ def deprecated(message):
return wrapper
def deprecation_map(dep_map):
"""Decorator for applying a map of deprecating arguments to a function.
The map connects deprecating arguments and their replacements. The
shell.py script uses this map to create mutually exclusive argument groups
in argparse and also prints a deprecation warning telling the user to
switch to the updated argument.
NOTE: This decorator MUST be the outermost in the chain of argument
decorators to work correctly.
Example usage:
>>> @deprecation_map({ "old-argument": "new-argument" })
... @args("old-argument", required=True)
... @args("new-argument", required=True)
... def do_command_line_stuff():
... pass
"""
def _decorator(func):
if not hasattr(func, 'arguments'):
return func
func.deprecated_groups = []
for old_param, new_param in dep_map.items():
old_info, new_info = None, None
required = False
for (args, kwargs) in func.arguments:
if old_param in args:
old_info = (args, kwargs)
# Old arguments shouldn't be required if they were not
# previously, so prioritize old requirement
if 'required' in kwargs:
required = kwargs['required']
# Set to false so argparse doesn't get angry
kwargs['required'] = False
elif new_param in args:
new_info = (args, kwargs)
kwargs['required'] = False
if old_info and new_info:
break
# Add a tuple of (old, new, required), which in turn is:
# ((old_args, old_kwargs), (new_args, new_kwargs), required)
func.deprecated_groups.append((old_info, new_info, required))
# Remove arguments that would be duplicated by the groups we made
func.arguments.remove(old_info)
func.arguments.remove(new_info)
return func
return _decorator
def arg(*args, **kwargs):
"""Decorator for CLI args.

View File

@ -459,6 +459,7 @@ class OpenStackMagnumShell(object):
desc = callback.__doc__ or ''
action_help = desc.strip()
arguments = getattr(callback, 'arguments', [])
group_args = getattr(callback, 'deprecated_groups', [])
subparser = (
subparsers.add_parser(command,
@ -471,6 +472,12 @@ class OpenStackMagnumShell(object):
action='help',
help=argparse.SUPPRESS,)
self.subcommands[command] = subparser
for (old_info, new_info, req) in group_args:
group = subparser.add_mutually_exclusive_group(required=req)
group.add_argument(*old_info[0], **old_info[1])
group.add_argument(*new_info[0], **new_info[1])
for (args, kwargs) in arguments:
subparser.add_argument(*args, **kwargs)
subparser.set_defaults(func=callback)
@ -630,8 +637,31 @@ class OpenStackMagnumShell(object):
api_version=args.magnum_api_version,
)
self._check_deprecation(args.func, argv)
args.func(self.cs, args)
def _check_deprecation(self, func, argv):
if not hasattr(func, 'deprecated_groups'):
return
for (old_info, new_info, required) in func.deprecated_groups:
old_param = old_info[0][0]
new_param = new_info[0][0]
old_value, new_value = None, None
for i in range(len(argv)):
cur_arg = argv[i]
if cur_arg == old_param:
old_value = argv[i + 1]
elif cur_arg == new_param[0]:
new_value = argv[i + 1]
if old_value and not new_value:
print(
'WARNING: The %s parameter is deprecated and will be '
'removed in a future release. Use the %s parameter to '
'avoid seeing this message.'
% (old_param, new_param))
def _dump_timings(self, timings):
class Tyme(object):
def __init__(self, url, seconds):