@@ -856,12 +856,15 @@ class Command:
856856 If enabled this will add ``--help`` as argument
857857 if no arguments are passed
858858 :param hidden: hide this command from help outputs.
859-
860- :param deprecated: issues a message indicating that
861- the command is deprecated.
859+ :param deprecated: If ``True`` or non-empty string, issues a message
860+ indicating that the command is deprecated and highlights
861+ its deprecation in --help. The message can be customized
862+ by using a string as the value.
862863
863864 .. versionchanged:: 8.2
864865 This is the base class for all commands, not ``BaseCommand``.
866+ ``deprecated`` can be set to a string as well to customize the
867+ deprecation message.
865868
866869 .. versionchanged:: 8.1
867870 ``help``, ``epilog``, and ``short_help`` are stored unprocessed,
@@ -905,7 +908,7 @@ def __init__(
905908 add_help_option : bool = True ,
906909 no_args_is_help : bool = False ,
907910 hidden : bool = False ,
908- deprecated : bool = False ,
911+ deprecated : bool | str = False ,
909912 ) -> None :
910913 #: the name the command thinks it has. Upon registering a command
911914 #: on a :class:`Group` the group will default the command name
@@ -1059,7 +1062,14 @@ def get_short_help_str(self, limit: int = 45) -> str:
10591062 text = ""
10601063
10611064 if self .deprecated :
1062- text = _ ("(Deprecated) {text}" ).format (text = text )
1065+ deprecated_message = (
1066+ f"(DEPRECATED: { self .deprecated } )"
1067+ if isinstance (self .deprecated , str )
1068+ else "(DEPRECATED)"
1069+ )
1070+ text = _ ("{text} {deprecated_message}" ).format (
1071+ text = text , deprecated_message = deprecated_message
1072+ )
10631073
10641074 return text .strip ()
10651075
@@ -1089,7 +1099,14 @@ def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None:
10891099 text = ""
10901100
10911101 if self .deprecated :
1092- text = _ ("(Deprecated) {text}" ).format (text = text )
1102+ deprecated_message = (
1103+ f"(DEPRECATED: { self .deprecated } )"
1104+ if isinstance (self .deprecated , str )
1105+ else "(DEPRECATED)"
1106+ )
1107+ text = _ ("{text} {deprecated_message}" ).format (
1108+ text = text , deprecated_message = deprecated_message
1109+ )
10931110
10941111 if text :
10951112 formatter .write_paragraph ()
@@ -1183,9 +1200,13 @@ def invoke(self, ctx: Context) -> t.Any:
11831200 in the right way.
11841201 """
11851202 if self .deprecated :
1203+ extra_message = (
1204+ f" { self .deprecated } " if isinstance (self .deprecated , str ) else ""
1205+ )
11861206 message = _ (
11871207 "DeprecationWarning: The command {name!r} is deprecated."
1188- ).format (name = self .name )
1208+ "{extra_message}"
1209+ ).format (name = self .name , extra_message = extra_message )
11891210 echo (style (message , fg = "red" ), err = True )
11901211
11911212 if self .callback is not None :
@@ -1988,6 +2009,18 @@ class Parameter:
19882009 given. Takes ``ctx, param, incomplete`` and must return a list
19892010 of :class:`~click.shell_completion.CompletionItem` or a list of
19902011 strings.
2012+ :param deprecated: If ``True`` or non-empty string, issues a message
2013+ indicating that the argument is deprecated and highlights
2014+ its deprecation in --help. The message can be customized
2015+ by using a string as the value. A deprecated parameter
2016+ cannot be required, a ValueError will be raised otherwise.
2017+
2018+ .. versionchanged:: 8.2.0
2019+ Introduction of ``deprecated``.
2020+
2021+ .. versionchanged:: 8.2
2022+ Adding duplicate parameter names to a :class:`~click.core.Command` will
2023+ result in a ``UserWarning`` being shown.
19912024
19922025 .. versionchanged:: 8.2
19932026 Adding duplicate parameter names to a :class:`~click.core.Command` will
@@ -2044,6 +2077,7 @@ def __init__(
20442077 [Context , Parameter , str ], list [CompletionItem ] | list [str ]
20452078 ]
20462079 | None = None ,
2080+ deprecated : bool | str = False ,
20472081 ) -> None :
20482082 self .name : str | None
20492083 self .opts : list [str ]
@@ -2071,6 +2105,7 @@ def __init__(
20712105 self .metavar = metavar
20722106 self .envvar = envvar
20732107 self ._custom_shell_complete = shell_complete
2108+ self .deprecated = deprecated
20742109
20752110 if __debug__ :
20762111 if self .type .is_composite and nargs != self .type .arity :
@@ -2113,6 +2148,13 @@ def __init__(
21132148 f"'default' { subject } must match nargs={ nargs } ."
21142149 )
21152150
2151+ if required and deprecated :
2152+ raise ValueError (
2153+ f"The { self .param_type_name } '{ self .human_readable_name } ' "
2154+ "is deprecated and still required. A deprecated "
2155+ f"{ self .param_type_name } cannot be required."
2156+ )
2157+
21162158 def to_info_dict (self ) -> dict [str , t .Any ]:
21172159 """Gather information that could be useful for a tool generating
21182160 user-facing documentation.
@@ -2332,6 +2374,29 @@ def handle_parse_result(
23322374 ) -> tuple [t .Any , list [str ]]:
23332375 with augment_usage_errors (ctx , param = self ):
23342376 value , source = self .consume_value (ctx , opts )
2377+
2378+ if (
2379+ self .deprecated
2380+ and value is not None
2381+ and source
2382+ not in (
2383+ ParameterSource .DEFAULT ,
2384+ ParameterSource .DEFAULT_MAP ,
2385+ )
2386+ ):
2387+ extra_message = (
2388+ f" { self .deprecated } " if isinstance (self .deprecated , str ) else ""
2389+ )
2390+ message = _ (
2391+ "DeprecationWarning: The {param_type} {name!r} is deprecated."
2392+ "{extra_message}"
2393+ ).format (
2394+ param_type = self .param_type_name ,
2395+ name = self .human_readable_name ,
2396+ extra_message = extra_message ,
2397+ )
2398+ echo (style (message , fg = "red" ), err = True )
2399+
23352400 ctx .set_parameter_source (self .name , source ) # type: ignore
23362401
23372402 try :
@@ -2402,7 +2467,8 @@ class Option(Parameter):
24022467 Normally, environment variables are not shown.
24032468 :param prompt: If set to ``True`` or a non empty string then the
24042469 user will be prompted for input. If set to ``True`` the prompt
2405- will be the option name capitalized.
2470+ will be the option name capitalized. A deprecated option cannot be
2471+ prompted.
24062472 :param confirmation_prompt: Prompt a second time to confirm the
24072473 value if it was prompted for. Can be set to a string instead of
24082474 ``True`` to customize the message.
@@ -2469,13 +2535,16 @@ def __init__(
24692535 hidden : bool = False ,
24702536 show_choices : bool = True ,
24712537 show_envvar : bool = False ,
2538+ deprecated : bool | str = False ,
24722539 ** attrs : t .Any ,
24732540 ) -> None :
24742541 if help :
24752542 help = inspect .cleandoc (help )
24762543
24772544 default_is_missing = "default" not in attrs
2478- super ().__init__ (param_decls , type = type , multiple = multiple , ** attrs )
2545+ super ().__init__ (
2546+ param_decls , type = type , multiple = multiple , deprecated = deprecated , ** attrs
2547+ )
24792548
24802549 if prompt is True :
24812550 if self .name is None :
@@ -2487,6 +2556,14 @@ def __init__(
24872556 else :
24882557 prompt_text = prompt
24892558
2559+ if deprecated :
2560+ deprecated_message = (
2561+ f"(DEPRECATED: { deprecated } )"
2562+ if isinstance (deprecated , str )
2563+ else "(DEPRECATED)"
2564+ )
2565+ help = help + deprecated_message if help is not None else deprecated_message
2566+
24902567 self .prompt = prompt_text
24912568 self .confirmation_prompt = confirmation_prompt
24922569 self .prompt_required = prompt_required
@@ -2548,6 +2625,9 @@ def __init__(
25482625 self .show_envvar = show_envvar
25492626
25502627 if __debug__ :
2628+ if deprecated and prompt :
2629+ raise ValueError ("`deprecated` options cannot use `prompt`." )
2630+
25512631 if self .nargs == - 1 :
25522632 raise TypeError ("nargs=-1 is not supported for options." )
25532633
@@ -2983,6 +3063,8 @@ def make_metavar(self) -> str:
29833063 var = self .type .get_metavar (self )
29843064 if not var :
29853065 var = self .name .upper () # type: ignore
3066+ if self .deprecated :
3067+ var += "!"
29863068 if not self .required :
29873069 var = f"[{ var } ]"
29883070 if self .nargs != 1 :
0 commit comments