3535)
3636
3737import typing_extensions
38- from pydantic import BaseModel
38+ from pydantic import BaseModel , Field
3939from pydantic ._internal ._repr import Representation
4040from pydantic ._internal ._utils import is_model_class
4141from pydantic .dataclasses import is_pydantic_dataclass
4747
4848from ...exceptions import SettingsError
4949from ...utils import _lenient_issubclass , _WithArgsTypes
50- from ..types import _CliExplicitFlag , _CliImplicitFlag , _CliPositionalArg , _CliSubCommand
50+ from ..types import NoDecode , _CliExplicitFlag , _CliImplicitFlag , _CliPositionalArg , _CliSubCommand , _CliUnknownArgs
5151from ..utils import (
5252 _annotation_contains_types ,
5353 _annotation_enum_val_to_name ,
@@ -86,6 +86,7 @@ class CliMutuallyExclusiveGroup(BaseModel):
8686CliExplicitFlag = Annotated [_CliBoolFlag , _CliExplicitFlag ]
8787CLI_SUPPRESS = SUPPRESS
8888CliSuppress = Annotated [T , CLI_SUPPRESS ]
89+ CliUnknownArgs = Annotated [list [str ], Field (default = []), _CliUnknownArgs , NoDecode ]
8990
9091
9192class CliSettingsSource (EnvSettingsSource , Generic [T ]):
@@ -364,6 +365,8 @@ def _load_env_vars(
364365 if not any (field_name for field_name in parsed_args .keys () if f'{ last_selected_subcommand } .' in field_name ):
365366 parsed_args [last_selected_subcommand ] = '{}'
366367
368+ parsed_args .update (self ._cli_unknown_args )
369+
367370 self .env_vars = parse_env_vars (
368371 cast (Mapping [str , str ], parsed_args ),
369372 self .case_sensitive ,
@@ -630,8 +633,13 @@ def _connect_root_parser(
630633 add_subparsers_method : Callable [..., Any ] | None = ArgumentParser .add_subparsers ,
631634 formatter_class : Any = RawDescriptionHelpFormatter ,
632635 ) -> None :
636+ self ._cli_unknown_args : dict [str , list [str ]] = {}
637+
633638 def _parse_known_args (* args : Any , ** kwargs : Any ) -> Namespace :
634- return ArgumentParser .parse_known_args (* args , ** kwargs )[0 ]
639+ args , unknown_args = ArgumentParser .parse_known_args (* args , ** kwargs )
640+ for dest in self ._cli_unknown_args :
641+ self ._cli_unknown_args [dest ] = unknown_args
642+ return cast (Namespace , args )
635643
636644 self ._root_parser = root_parser
637645 if parse_args_method is None :
@@ -755,10 +763,7 @@ def _add_parser_args(
755763 if not arg_names or (kwargs ['dest' ] in added_args ):
756764 continue
757765
758- if is_append_action :
759- kwargs ['action' ] = 'append'
760- if _annotation_contains_types (field_info .annotation , (dict , Mapping ), is_strip_annotated = True ):
761- self ._cli_dict_args [kwargs ['dest' ]] = field_info .annotation
766+ self ._convert_append_action (kwargs , field_info , is_append_action )
762767
763768 if _CliPositionalArg in field_info .metadata :
764769 arg_names , flag_prefix = self ._convert_positional_arg (
@@ -783,6 +788,8 @@ def _add_parser_args(
783788 alias_names ,
784789 model_default = model_default ,
785790 )
791+ elif _CliUnknownArgs in field_info .metadata :
792+ self ._cli_unknown_args [kwargs ['dest' ]] = []
786793 elif not is_alias_path_only :
787794 if group is not None :
788795 if isinstance (group , dict ):
@@ -805,6 +812,12 @@ def _check_kebab_name(self, name: str) -> str:
805812 return name .replace ('_' , '-' )
806813 return name
807814
815+ def _convert_append_action (self , kwargs : dict [str , Any ], field_info : FieldInfo , is_append_action : bool ) -> None :
816+ if is_append_action :
817+ kwargs ['action' ] = 'append'
818+ if _annotation_contains_types (field_info .annotation , (dict , Mapping ), is_strip_annotated = True ):
819+ self ._cli_dict_args [kwargs ['dest' ]] = field_info .annotation
820+
808821 def _convert_bool_flag (self , kwargs : dict [str , Any ], field_info : FieldInfo , model_default : Any ) -> None :
809822 if kwargs ['metavar' ] == 'bool' :
810823 if (self .cli_implicit_flags or _CliImplicitFlag in field_info .metadata ) and (
0 commit comments