bpo-39467: allow user to deprecate CLI arguments
This commit is contained in:
parent
b9783d2e03
commit
7c1d2bedda
|
@ -698,6 +698,15 @@ The add_argument() method
|
|||
* default_ - The value produced if the argument is absent from the
|
||||
command line.
|
||||
|
||||
* deprecated_ - Define if the argument is deprecated.
|
||||
|
||||
* deprecated_reason_ - Custome deprecation warning message to display if
|
||||
the argument is deprecated.
|
||||
|
||||
* deprecated_pending_ - Define if the deprecation is pending. The argument
|
||||
is obsolete and expected to be deprecated in the future, but is not
|
||||
deprecated at the moment.
|
||||
|
||||
* type_ - The type to which the command-line argument should be converted.
|
||||
|
||||
* choices_ - A container of the allowable values for the argument.
|
||||
|
@ -1052,6 +1061,72 @@ command-line argument was not present::
|
|||
Namespace(foo='1')
|
||||
|
||||
|
||||
deprecated
|
||||
^^^^^^^^^^
|
||||
|
||||
During projects lifecycle some arguments could be removed from the
|
||||
command line, before removing these arguments definitively you would inform
|
||||
your user that arguments are deprecated and will be removed.
|
||||
The ``deprecated`` keyword argument of
|
||||
:meth:`~ArgumentParser.add_argument`, whose value default to ``False``,
|
||||
specifies if the argument is deprecated and will be removed
|
||||
from the command-line available arguments in the future.
|
||||
For arguments, if ``deprecated`` is ``True`` then a warning (``DeprecationWarning``) will be
|
||||
emitted if the argument is given by user in the command line parameters::
|
||||
|
||||
>>> import argparse
|
||||
>>> parser = argparse.ArgumentParser()
|
||||
>>> parser.add_argument('bar', default=1)
|
||||
>>> parser.add_argument('--foo', default=2, deprecated=True)
|
||||
>>> parser.parse_args(['test'])
|
||||
Namespace(bar='test', foo='2')
|
||||
>>> parser.parse_args(['test', '--foo', '4'])
|
||||
/home/cpython/Lib/argparse.py:1979: DeprecationWarning: Usage of parameter foo are deprecated
|
||||
Namespace(bar='test', foo='4')
|
||||
|
||||
|
||||
deprecated_reason
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Custome deprecation warning message to display if the argument is deprecated.
|
||||
If not given then a standard message will be displayed.
|
||||
The ``deprecated_reason`` keyword argument of
|
||||
:meth:`~ArgumentParser.add_argument`, allow to define a custome message to
|
||||
display if the argument is deprecated and given by user in the command line parameters::
|
||||
|
||||
>>> import argparse
|
||||
>>> parser = argparse.ArgumentParser()
|
||||
>>> parser.add_argument('bar', default=1)
|
||||
>>> parser.add_argument('--foo', default=2, deprecated=True, deprecated_reason='my custom message')
|
||||
>>> parser.parse_args(['test'])
|
||||
Namespace(bar='test', foo='2')
|
||||
>>> parser.parse_args(['test', '--foo', '4'])
|
||||
/home/cpython/Lib/argparse.py:1979: DeprecationWarning: my custome message
|
||||
Namespace(bar='test', foo='4')
|
||||
|
||||
deprecated_pending
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Define if the deprecation is pending. Could be used to define that an argument
|
||||
is obsolete and expected to be deprecated in the future, but is not
|
||||
deprecated at the moment.
|
||||
The ``deprecated_pending`` keyword argument of
|
||||
:meth:`~ArgumentParser.add_argument`, whose value default to ``False``,
|
||||
specifies if the argument is obsolete and expected to be deprecated in the future.
|
||||
For arguments, if ``deprecated_pending`` is ``True`` then a warning
|
||||
(``PendingDeprecationWarning``) will be emitted if the argument is given by
|
||||
user in the command line parameters::
|
||||
|
||||
>>> import argparse
|
||||
>>> parser = argparse.ArgumentParser()
|
||||
>>> parser.add_argument('bar', default=1)
|
||||
>>> parser.add_argument('--foo', default=2, deprecated=True, deprecated_pending=True)
|
||||
>>> parser.parse_args(['test'])
|
||||
Namespace(bar='test', foo='2')
|
||||
>>> parser.parse_args(['test', '--foo', '4'])
|
||||
/home/cpython/Lib/argparse.py:1979: PendingDeprecationWarning: The argument foo is obsolete and expected to be deprecated in the future
|
||||
Namespace(bar='test', foo='4')
|
||||
|
||||
type
|
||||
^^^^
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ __all__ = [
|
|||
import os as _os
|
||||
import re as _re
|
||||
import sys as _sys
|
||||
import warnings
|
||||
|
||||
from gettext import gettext as _, ngettext
|
||||
|
||||
|
@ -817,6 +818,9 @@ class Action(_AttributeHolder):
|
|||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
|
@ -827,6 +831,14 @@ class Action(_AttributeHolder):
|
|||
self.nargs = nargs
|
||||
self.const = const
|
||||
self.default = default
|
||||
self.deprecated = deprecated
|
||||
if not deprecated_reason:
|
||||
deprecated_reason = f"Usage of parameter {dest} are deprecated"
|
||||
if deprecated_pending:
|
||||
deprecated_reason = f"The argument {dest} is obsolete and " \
|
||||
"expected to be deprecated in the future"
|
||||
self.deprecated_reason = deprecated_reason
|
||||
self.deprecated_pending=deprecated_pending
|
||||
self.type = type
|
||||
self.choices = choices
|
||||
self.required = required
|
||||
|
@ -840,6 +852,9 @@ class Action(_AttributeHolder):
|
|||
'nargs',
|
||||
'const',
|
||||
'default',
|
||||
'deprecated',
|
||||
'deprecated_reason',
|
||||
'deprecated_pending',
|
||||
'type',
|
||||
'choices',
|
||||
'help',
|
||||
|
@ -859,6 +874,9 @@ class BooleanOptionalAction(Action):
|
|||
dest,
|
||||
const=None,
|
||||
default=None,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
|
@ -881,6 +899,9 @@ class BooleanOptionalAction(Action):
|
|||
dest=dest,
|
||||
nargs=0,
|
||||
default=default,
|
||||
deprecated=deprecated,
|
||||
deprecated_reason=deprecated_reason,
|
||||
deprecated_pending=deprecated_pending,
|
||||
type=type,
|
||||
choices=choices,
|
||||
required=required,
|
||||
|
@ -903,6 +924,9 @@ class _StoreAction(Action):
|
|||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
|
@ -920,6 +944,9 @@ class _StoreAction(Action):
|
|||
nargs=nargs,
|
||||
const=const,
|
||||
default=default,
|
||||
deprecated=deprecated,
|
||||
deprecated_reason=deprecated_reason,
|
||||
deprecated_pending=deprecated_pending,
|
||||
type=type,
|
||||
choices=choices,
|
||||
required=required,
|
||||
|
@ -937,6 +964,9 @@ class _StoreConstAction(Action):
|
|||
dest,
|
||||
const,
|
||||
default=None,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None):
|
||||
|
@ -946,6 +976,9 @@ class _StoreConstAction(Action):
|
|||
nargs=0,
|
||||
const=const,
|
||||
default=default,
|
||||
deprecated=deprecated,
|
||||
deprecated_reason=deprecated_reason,
|
||||
deprecated_pending=deprecated_pending,
|
||||
required=required,
|
||||
help=help)
|
||||
|
||||
|
@ -959,6 +992,9 @@ class _StoreTrueAction(_StoreConstAction):
|
|||
option_strings,
|
||||
dest,
|
||||
default=False,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
required=False,
|
||||
help=None):
|
||||
super(_StoreTrueAction, self).__init__(
|
||||
|
@ -966,6 +1002,9 @@ class _StoreTrueAction(_StoreConstAction):
|
|||
dest=dest,
|
||||
const=True,
|
||||
default=default,
|
||||
deprecated=deprecated,
|
||||
deprecated_reason=deprecated_reason,
|
||||
deprecated_pending=deprecated_pending,
|
||||
required=required,
|
||||
help=help)
|
||||
|
||||
|
@ -976,6 +1015,9 @@ class _StoreFalseAction(_StoreConstAction):
|
|||
option_strings,
|
||||
dest,
|
||||
default=True,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
required=False,
|
||||
help=None):
|
||||
super(_StoreFalseAction, self).__init__(
|
||||
|
@ -983,6 +1025,9 @@ class _StoreFalseAction(_StoreConstAction):
|
|||
dest=dest,
|
||||
const=False,
|
||||
default=default,
|
||||
deprecated=deprecated,
|
||||
deprecated_reason=deprecated_reason,
|
||||
deprecated_pending=deprecated_pending,
|
||||
required=required,
|
||||
help=help)
|
||||
|
||||
|
@ -995,6 +1040,9 @@ class _AppendAction(Action):
|
|||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
|
@ -1012,6 +1060,9 @@ class _AppendAction(Action):
|
|||
nargs=nargs,
|
||||
const=const,
|
||||
default=default,
|
||||
deprecated=deprecated,
|
||||
deprecated_reason=deprecated_reason,
|
||||
deprecated_pending=deprecated_pending,
|
||||
type=type,
|
||||
choices=choices,
|
||||
required=required,
|
||||
|
@ -1032,6 +1083,9 @@ class _AppendConstAction(Action):
|
|||
dest,
|
||||
const,
|
||||
default=None,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None):
|
||||
|
@ -1041,6 +1095,9 @@ class _AppendConstAction(Action):
|
|||
nargs=0,
|
||||
const=const,
|
||||
default=default,
|
||||
deprecated=deprecated,
|
||||
deprecated_reason=deprecated_reason,
|
||||
deprecated_pending=deprecated_pending,
|
||||
required=required,
|
||||
help=help,
|
||||
metavar=metavar)
|
||||
|
@ -1058,6 +1115,9 @@ class _CountAction(Action):
|
|||
option_strings,
|
||||
dest,
|
||||
default=None,
|
||||
deprecated=False,
|
||||
deprecated_reason=None,
|
||||
deprecated_pending=False,
|
||||
required=False,
|
||||
help=None):
|
||||
super(_CountAction, self).__init__(
|
||||
|
@ -1065,6 +1125,9 @@ class _CountAction(Action):
|
|||
dest=dest,
|
||||
nargs=0,
|
||||
default=default,
|
||||
deprecated=deprecated,
|
||||
deprecated_reason=deprecated_reason,
|
||||
deprecated_pending=deprecated_pending,
|
||||
required=required,
|
||||
help=help)
|
||||
|
||||
|
@ -1901,6 +1964,18 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
|||
pattern = 'O'
|
||||
arg_string_pattern_parts.append(pattern)
|
||||
|
||||
# check if the arg is deprecated
|
||||
# and warn only if it's given in the CLI paramters
|
||||
for action in self._actions:
|
||||
if arg_string.replace("-", "") == action.dest:
|
||||
if not action.deprecated:
|
||||
continue
|
||||
warnings.warn(
|
||||
action.deprecated_reason,
|
||||
PendingDeprecationWarning \
|
||||
if action.deprecated_pending \
|
||||
else DeprecationWarning)
|
||||
|
||||
# join the pieces together to form the pattern
|
||||
arg_strings_pattern = ''.join(arg_string_pattern_parts)
|
||||
|
||||
|
|
|
@ -4660,12 +4660,17 @@ class TestStrings(TestCase):
|
|||
type='int',
|
||||
nargs='+',
|
||||
default=42,
|
||||
deprecated=False,
|
||||
deprecated_reason='foo bar',
|
||||
deprecated_pending=False,
|
||||
choices=[1, 2, 3],
|
||||
help='HELP',
|
||||
metavar='METAVAR')
|
||||
string = (
|
||||
"Action(option_strings=['--foo', '-a', '-b'], dest='b', "
|
||||
"nargs='+', const=None, default=42, type='int', "
|
||||
"nargs='+', const=None, default=42, deprecated=False, "
|
||||
"deprecated_reason='foo bar', "
|
||||
"deprecated_pending=False, type='int', "
|
||||
"choices=[1, 2, 3], help='HELP', metavar='METAVAR')")
|
||||
self.assertStringEqual(option, string)
|
||||
|
||||
|
@ -4676,12 +4681,18 @@ class TestStrings(TestCase):
|
|||
type=float,
|
||||
nargs='?',
|
||||
default=2.5,
|
||||
deprecated=False,
|
||||
deprecated_reason='foo bar',
|
||||
deprecated_pending=False,
|
||||
choices=[0.5, 1.5, 2.5],
|
||||
help='H HH H',
|
||||
metavar='MV MV MV')
|
||||
string = (
|
||||
"Action(option_strings=[], dest='x', nargs='?', "
|
||||
"const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], "
|
||||
"const=None, default=2.5, deprecated=False, "
|
||||
"deprecated_reason='foo bar', "
|
||||
"deprecated_pending=False, "
|
||||
"type=%r, choices=[0.5, 1.5, 2.5], "
|
||||
"help='H HH H', metavar='MV MV MV')" % float)
|
||||
self.assertStringEqual(argument, string)
|
||||
|
||||
|
@ -4874,6 +4885,39 @@ class TestTypeFunctionCallOnlyOnce(TestCase):
|
|||
args = parser.parse_args('--foo spam!'.split())
|
||||
self.assertEqual(NS(foo='foo_converted'), args)
|
||||
|
||||
|
||||
# =============================================
|
||||
# Check that deprecated arguments raise warning
|
||||
# =============================================
|
||||
|
||||
class TestTypeFunctionCallWithDeprecated(TestCase):
|
||||
|
||||
def test_type_function_call_with_deprecated(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--foo', deprecated=True, default='bar')
|
||||
with support.captured_stderr() as stderr:
|
||||
parser.parse_args(['--foo', 'spam'])
|
||||
stderr = stderr.getvalue()
|
||||
self.assertIn("DeprecationWarning", stderr)
|
||||
|
||||
def test_type_function_call_with_pending_deprecated(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--foo', deprecated=True,
|
||||
deprecated_pending=True, default='bar')
|
||||
with support.captured_stderr() as stderr:
|
||||
parser.parse_args(['--foo', 'spam'])
|
||||
stderr = stderr.getvalue()
|
||||
self.assertIn("PendingDeprecationWarning", stderr)
|
||||
|
||||
def test_type_function_call_with_deprecated_custome_msg(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--foo', deprecated=True,
|
||||
deprecated_reason="foo bar", default='bar')
|
||||
with support.captured_stderr() as stderr:
|
||||
parser.parse_args(['--foo', 'spam'])
|
||||
stderr = stderr.getvalue()
|
||||
self.assertIn("DeprecationWarning: foo bar", stderr)
|
||||
|
||||
# ==================================================================
|
||||
# Check semantics regarding the default argument and type conversion
|
||||
# ==================================================================
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Allow to deprecate CLI arguments with argparse
|
Loading…
Reference in New Issue