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 :
@@ -756,10 +764,7 @@ def _add_parser_args(
756764 if not arg_names or (kwargs ['dest' ] in added_args ):
757765 continue
758766
759- if is_append_action :
760- kwargs ['action' ] = 'append'
761- if _annotation_contains_types (field_info .annotation , (dict , Mapping ), is_strip_annotated = True ):
762- self ._cli_dict_args [kwargs ['dest' ]] = field_info .annotation
767+ self ._convert_append_action (kwargs , field_info , is_append_action )
763768
764769 if _CliPositionalArg in field_info .metadata :
765770 arg_names , flag_prefix = self ._convert_positional_arg (
@@ -785,6 +790,8 @@ def _add_parser_args(
785790 model_default = model_default ,
786791 is_model_suppressed = is_model_suppressed ,
787792 )
793+ elif _CliUnknownArgs in field_info .metadata :
794+ self ._cli_unknown_args [kwargs ['dest' ]] = []
788795 elif not is_alias_path_only :
789796 if group is not None :
790797 if isinstance (group , dict ):
@@ -807,6 +814,12 @@ def _check_kebab_name(self, name: str) -> str:
807814 return name .replace ('_' , '-' )
808815 return name
809816
817+ def _convert_append_action (self , kwargs : dict [str , Any ], field_info : FieldInfo , is_append_action : bool ) -> None :
818+ if is_append_action :
819+ kwargs ['action' ] = 'append'
820+ if _annotation_contains_types (field_info .annotation , (dict , Mapping ), is_strip_annotated = True ):
821+ self ._cli_dict_args [kwargs ['dest' ]] = field_info .annotation
822+
810823 def _convert_bool_flag (self , kwargs : dict [str , Any ], field_info : FieldInfo , model_default : Any ) -> None :
811824 if kwargs ['metavar' ] == 'bool' :
812825 if (self .cli_implicit_flags or _CliImplicitFlag in field_info .metadata ) and (
0 commit comments