@@ -25,7 +25,7 @@ class InterfacyLayout(HelpLayout):
2525
2626 column_gap : str = " "
2727 format_option : str | None = "{flag_col}{column_gap}{description}{extra}"
28- format_positional : str | None = "{flag_col}{column_gap}{description}"
28+ format_positional : str | None = "{flag_col}{column_gap}{description}{extra} "
2929 include_metavar_in_flag_display : bool = False
3030 layout_mode : Literal ["auto" , "adaptive" , "template" ] = "template"
3131 required_indicator : str = "(" + colored ("*" , color = "red" ) + ")"
@@ -704,6 +704,19 @@ def _description_mentions_same_default(cls, description: str, default_text: str)
704704
705705 return f"[default: { normalized_default } ]" in normalized_description
706706
707+ @classmethod
708+ def _description_mentions_same_choices (
709+ cls ,
710+ description : str ,
711+ choices_text : str ,
712+ ) -> bool :
713+ if not description or not choices_text :
714+ return False
715+
716+ normalized_description = cls ._normalize_whitespace (description ).lower ()
717+ normalized_choices = cls ._normalize_whitespace (choices_text ).lower ()
718+ return f"[choices: { normalized_choices } ]" in normalized_description
719+
707720 @classmethod
708721 def _with_default_sentence (cls , description : str , has_default : bool , default : object ) -> str :
709722 if not has_default :
@@ -721,6 +734,24 @@ def _with_default_sentence(cls, description: str, has_default: bool, default: ob
721734 return default_block
722735 return f"{ description .rstrip ()} { default_block } "
723736
737+ @classmethod
738+ def _with_choices_block (
739+ cls ,
740+ description : str ,
741+ choices : Sequence [object ] | None ,
742+ ) -> str :
743+ if not choices :
744+ return description
745+
746+ choices_text = ", " .join (str (choice ) for choice in choices )
747+ if cls ._description_mentions_same_choices (description , choices_text ):
748+ return description
749+
750+ choices_block = f"[choices: { choices_text } ]"
751+ if not description :
752+ return choices_block
753+ return f"{ description .rstrip ()} { choices_block } "
754+
724755 def get_help_for_parameter (
725756 self ,
726757 param : Parameter ,
@@ -747,7 +778,9 @@ def get_help_for_parameter(
747778 long_flag = primary_flag ,
748779 )
749780 )
750- return self ._with_default_sentence (description , has_default , default_value )
781+ description = self ._with_default_sentence (description , has_default , default_value )
782+ choices = get_param_choices (param , for_display = True ) if param .is_typed else None
783+ return self ._with_choices_block (description , choices )
751784
752785 def format_argument (
753786 self ,
@@ -769,7 +802,13 @@ def format_argument(
769802 long_flag = self ._get_primary_boolean_flag_from_argument (arg ),
770803 )
771804 )
772- return self ._with_default_sentence (description , has_default , default_value )
805+ description = self ._with_default_sentence (description , has_default , default_value )
806+ choices = (
807+ [self ._format_argument_choice_for_help (arg , choice ) for choice in arg .choices ]
808+ if arg .choices
809+ else None
810+ )
811+ return self ._with_choices_block (description , choices )
773812
774813
775814@dataclass (kw_only = True )
@@ -818,25 +857,63 @@ def _description_mentions_same_default(cls, description: str, default_text: str)
818857 )
819858
820859 @classmethod
821- def _with_default_sentence (cls , description : str , has_default : bool , default : object ) -> str :
822- if not has_default :
860+ def _description_mentions_same_choices (
861+ cls ,
862+ description : str ,
863+ choices_text : str ,
864+ ) -> bool :
865+ if not description or not choices_text :
866+ return False
867+
868+ normalized_description = cls ._normalize_whitespace (description ).lower ()
869+ normalized_choices = cls ._normalize_whitespace (choices_text ).lower ()
870+ return (
871+ f"choices: { normalized_choices } " in normalized_description
872+ or f"possible values: { normalized_choices } " in normalized_description
873+ )
874+
875+ @classmethod
876+ def _append_sentence (cls , description : str , sentence : str ) -> str :
877+ if not sentence :
823878 return description
824879
825880 description = cls ._collapse_duplicate_terminal_period (description )
826881
882+ separator = ""
883+ if description :
884+ separator = " " if description .rstrip ().endswith (("." , "?" , "!" , ":" , ";" )) else ". "
885+
886+ terminal = "" if sentence .endswith (("." , "?" , "!" , ":" , ";" )) else "."
887+ return f"{ description } { separator } { sentence } { terminal } "
888+
889+ @classmethod
890+ def _with_default_sentence (cls , description : str , has_default : bool , default : object ) -> str :
891+ if not has_default :
892+ return description
893+
827894 default_text = format_default_for_help (default )
828895 if default_text in {"" , '""' }:
829896 default_text = "''"
830897
831898 if cls ._description_mentions_same_default (description , default_text ):
832899 return description
833900
834- separator = ""
835- if description :
836- separator = " " if description .rstrip ().endswith (("." , "?" , "!" , ":" , ";" )) else ". "
901+ return cls ._append_sentence (description , f"Defaults to { default_text } " )
837902
838- terminal = "" if default_text .endswith (("." , "?" , "!" , ":" , ";" )) else "."
839- return f"{ description } { separator } Defaults to { default_text } { terminal } "
903+ @classmethod
904+ def _with_choices_sentence (
905+ cls ,
906+ description : str ,
907+ choices : Sequence [object ] | None ,
908+ ) -> str :
909+ if not choices :
910+ return description
911+
912+ choices_text = ", " .join (str (choice ) for choice in choices )
913+ if cls ._description_mentions_same_choices (description , choices_text ):
914+ return description
915+
916+ return cls ._append_sentence (description , f"Choices: { choices_text } " )
840917
841918 def get_help_for_parameter (
842919 self ,
@@ -864,7 +941,9 @@ def get_help_for_parameter(
864941 long_flag = primary_flag ,
865942 )
866943 )
867- return self ._with_default_sentence (description , has_default , default_value )
944+ description = self ._with_default_sentence (description , has_default , default_value )
945+ choices = get_param_choices (param , for_display = True ) if param .is_typed else None
946+ return self ._with_choices_sentence (description , choices )
868947
869948 def format_argument (
870949 self ,
@@ -886,7 +965,13 @@ def format_argument(
886965 long_flag = self ._get_primary_boolean_flag_from_argument (arg ),
887966 )
888967 )
889- return self ._with_default_sentence (description , has_default , default_value )
968+ description = self ._with_default_sentence (description , has_default , default_value )
969+ choices = (
970+ [self ._format_argument_choice_for_help (arg , choice ) for choice in arg .choices ]
971+ if arg .choices
972+ else None
973+ )
974+ return self ._with_choices_sentence (description , choices )
890975
891976
892977# Backward compatibility alias.
0 commit comments