@@ -473,13 +473,13 @@ models. There are two primary use cases for Pydantic settings CLI:
473
473
2 . When using Pydantic models to define CLIs.
474
474
475
475
By default, the experience is tailored towards use case #1 and builds on the foundations established in [ parsing
476
- environment variables] ( #parsing-environment-variables ) . If your use case primarily falls into #2 , you will likely want
477
- to enable [ enforcing required arguments at the CLI] ( #enforce-required-arguments-at-cli ) .
476
+ environment variables] ( #parsing-environment-variable-values ) . If your use case primarily falls into #2 , you will likely
477
+ want to enable [ enforcing required arguments at the CLI] ( #enforce-required-arguments-at-cli ) .
478
478
479
479
### The Basics
480
480
481
- To get started, let's revisit the example presented in [ parsing environment variables ] ( #parsing-environment-variables )
482
- but using a Pydantic settings CLI:
481
+ To get started, let's revisit the example presented in [ parsing environment
482
+ variables ] ( #parsing-environment-variable-values ) but using a Pydantic settings CLI:
483
483
484
484
``` py
485
485
import sys
@@ -528,16 +528,16 @@ print(Settings().model_dump())
528
528
To enable CLI parsing, we simply set the ` cli_parse_args ` flag to a valid value, which retains similar conotations as
529
529
defined in ` argparse ` . Alternatively, we can also directly provided the args to parse at time of instantiation:
530
530
531
- ``` py test="skip" lint="skip"
532
- Settings(
533
- _cli_parse_args = [
534
- ' --v0=0 ' ,
535
- ' --sub_model={"v1": "json-1", "v2": "json-2"} ' ,
536
- ' --sub_model.v2=nested-2 ' ,
537
- ' --sub_model.v3=3 ' ,
538
- ' --sub_model.deep.v4=v4 ' ,
539
- ]
540
- )
531
+ ``` py
532
+ from pydantic_settings import BaseSettings
533
+
534
+
535
+ class Settings ( BaseSettings ):
536
+ this_foo: str
537
+
538
+
539
+ print (Settings( _cli_parse_args = [ ' --this_foo ' , ' is such a foo ' ]).model_dump())
540
+ # > {'this_foo': 'is such a foo'}
541
541
```
542
542
543
543
Note that a CLI settings source is [ ** the topmost source** ] ( #field-value-priority ) by default unless its [ priority value
@@ -705,7 +705,7 @@ print(User().model_dump())
705
705
706
706
Subcommands and positional arguments are expressed using the ` CliSubCommand ` and ` CliPositionalArg ` annotations. These
707
707
annotations can only be applied to required fields (i.e. fields that do not have a default value). Furthermore,
708
- subcommands must be a valid type derived from the pydantic ` BaseModel ` class .
708
+ subcommands must be a valid type derived from either a pydantic ` BaseModel ` or pydantic.dataclasses ` dataclass ` .
709
709
710
710
!!! note
711
711
CLI settings subcommands are limited to a single subparser per model. In other words, all subcommands for a model
@@ -720,6 +720,7 @@ subcommands must be a valid type derived from the pydantic `BaseModel` class.
720
720
import sys
721
721
722
722
from pydantic import BaseModel, Field
723
+ from pydantic.dataclasses import dataclass
723
724
724
725
from pydantic_settings import (
725
726
BaseSettings,
@@ -728,51 +729,45 @@ from pydantic_settings import (
728
729
)
729
730
730
731
731
- class FooPlugin (BaseModel ):
732
+ @dataclass
733
+ class FooPlugin :
732
734
""" git-plugins-foo - Extra deep foo plugin command"""
733
735
734
- my_feature: bool = Field(
735
- default = False , description = ' Enable my feature on foo plugin'
736
- )
736
+ x_feature: bool = Field(default = False , description = ' Enable "X" feature' )
737
737
738
738
739
- class BarPlugin (BaseModel ):
739
+ @dataclass
740
+ class BarPlugin :
740
741
""" git-plugins-bar - Extra deep bar plugin command"""
741
742
742
- my_feature: bool = Field(
743
- default = False , description = ' Enable my feature on bar plugin'
744
- )
743
+ y_feature: bool = Field(default = False , description = ' Enable "Y" feature' )
745
744
746
745
747
- class Plugins (BaseModel ):
746
+ @dataclass
747
+ class Plugins :
748
748
""" git-plugins - Fake plugins for GIT"""
749
749
750
750
foo: CliSubCommand[FooPlugin] = Field(description = ' Foo is fake plugin' )
751
751
752
- bar: CliSubCommand[BarPlugin] = Field(description = ' Bar is also a fake plugin' )
752
+ bar: CliSubCommand[BarPlugin] = Field(description = ' Bar is fake plugin' )
753
753
754
754
755
755
class Clone (BaseModel ):
756
756
""" git-clone - Clone a repository into a new directory"""
757
757
758
- repository: CliPositionalArg[str ] = Field(description = ' The repository to clone ' )
758
+ repository: CliPositionalArg[str ] = Field(description = ' The repo ... ' )
759
759
760
- directory: CliPositionalArg[str ] = Field(description = ' The directory to clone into ' )
760
+ directory: CliPositionalArg[str ] = Field(description = ' The dir ... ' )
761
761
762
- local: bool = Field(
763
- default = False ,
764
- description = ' When the resposity to clone from is on a local machine, bypass ...' ,
765
- )
762
+ local: bool = Field(default = False , description = ' When the repo ...' )
766
763
767
764
768
765
class Git (BaseSettings , cli_parse_args = True , cli_prog_name = ' git' ):
769
766
""" git - The stupid content tracker"""
770
767
771
- clone: CliSubCommand[Clone] = Field(
772
- description = ' Clone a repository into a new directory'
773
- )
768
+ clone: CliSubCommand[Clone] = Field(description = ' Clone a repo ...' )
774
769
775
- plugins: CliSubCommand[Plugins] = Field(description = ' Fake GIT plugin commands ' )
770
+ plugins: CliSubCommand[Plugins] = Field(description = ' Fake GIT plugins ' )
776
771
777
772
778
773
try :
@@ -787,12 +782,12 @@ usage: git [-h] {clone,plugins} ...
787
782
git - The stupid content tracker
788
783
789
784
options:
790
- -h, --help show this help message and exit
785
+ -h, --help show this help message and exit
791
786
792
787
subcommands:
793
788
{clone,plugins}
794
- clone Clone a repository into a new directory
795
- plugins Fake GIT plugin commands
789
+ clone Clone a repo ...
790
+ plugins Fake GIT plugins
796
791
"""
797
792
798
793
@@ -808,12 +803,12 @@ usage: git clone [-h] [--local bool] [--shared bool] REPOSITORY DIRECTORY
808
803
git-clone - Clone a repository into a new directory
809
804
810
805
positional arguments:
811
- REPOSITORY The repository to clone
812
- DIRECTORY The directory to clone into
806
+ REPOSITORY The repo ...
807
+ DIRECTORY The dir ...
813
808
814
809
options:
815
- -h, --help show this help message and exit
816
- --local bool When the resposity to clone from is on a local machine, bypass ... (default: False)
810
+ -h, --help show this help message and exit
811
+ --local bool When the repo ... (default: False)
817
812
"""
818
813
819
814
@@ -829,8 +824,8 @@ usage: git plugins bar [-h] [--my_feature bool]
829
824
git-plugins-bar - Extra deep bar plugin command
830
825
831
826
options:
832
- -h, --help show this help message and exit
833
- --my_feature bool Enable my feature on bar plugin (default: False)
827
+ -h, --help show this help message and exit
828
+ --y_feature bool Enable "Y" feature (default: False)
834
829
"""
835
830
```
836
831
@@ -843,7 +838,7 @@ The below flags can be used to customise the CLI experience to your needs.
843
838
Change the default program name displayed in the help text usage by setting ` cli_prog_name ` . By default, it will derive
844
839
the name of the currently executing program from ` sys.argv[0] ` , just like argparse.
845
840
846
- ``` py test="skip"
841
+ ``` py
847
842
import sys
848
843
849
844
from pydantic_settings import BaseSettings
@@ -853,8 +848,12 @@ class Settings(BaseSettings, cli_parse_args=True, cli_prog_name='appdantic'):
853
848
pass
854
849
855
850
856
- sys.argv = [' example.py' , ' --help' ]
857
- Settings()
851
+ try :
852
+ sys.argv = [' example.py' , ' --help' ]
853
+ Settings()
854
+ except SystemExit as e:
855
+ print (e)
856
+ # > 0
858
857
"""
859
858
usage: appdantic [-h]
860
859
@@ -870,7 +869,7 @@ is required is not strictly required from any single source (e.g. the CLI). Inst
870
869
sources provides the required value.
871
870
872
871
However, if your use case [ aligns more with #2 ] ( #command-line-support ) , using Pydantic models to define CLIs, you will
873
- likely want required fields to be _ strictly required at the CLI_ . We can enable this behavior by using the
872
+ likely want required fields to be _ strictly required at the CLI_ . We can enable this behavior by using
874
873
` cli_enforce_required ` .
875
874
876
875
``` py
@@ -902,7 +901,7 @@ example.py: error: the following arguments are required: --my_required_field
902
901
903
902
#### Change the None Type Parse String
904
903
905
- Change the CLI string value that will be parsed (e.g. "null", "void", "None", etc.) into ` None ` type(None) by setting
904
+ Change the CLI string value that will be parsed (e.g. "null", "void", "None", etc.) into ` None ` by setting
906
905
` cli_parse_none_str ` . By default it will use the ` env_parse_none_str ` value if set. Otherwise, it will default to "null"
907
906
if ` cli_avoid_json ` is ` False ` , and "None" if ` cli_avoid_json ` is ` True ` .
908
907
@@ -928,7 +927,7 @@ print(Settings().model_dump())
928
927
929
928
Hide ` None ` values from the CLI help text by enabling ` cli_hide_none_type ` .
930
929
931
- ``` py test="skip"
930
+ ``` py
932
931
import sys
933
932
from typing import Optional
934
933
@@ -941,8 +940,12 @@ class Settings(BaseSettings, cli_parse_args=True, cli_hide_none_type=True):
941
940
v0: Optional[str ] = Field(description = ' the top level v0 option' )
942
941
943
942
944
- sys.argv = [' example.py' , ' --help' ]
945
- Settings()
943
+ try :
944
+ sys.argv = [' example.py' , ' --help' ]
945
+ Settings()
946
+ except SystemExit as e:
947
+ print (e)
948
+ # > 0
946
949
"""
947
950
usage: example.py [-h] [--v0 str]
948
951
@@ -956,7 +959,7 @@ options:
956
959
957
960
Avoid adding complex fields that result in JSON strings at the CLI by enabling ` cli_avoid_json ` .
958
961
959
- ``` py test="skip"
962
+ ``` py
960
963
import sys
961
964
962
965
from pydantic import BaseModel, Field
@@ -974,8 +977,12 @@ class Settings(BaseSettings, cli_parse_args=True, cli_avoid_json=True):
974
977
)
975
978
976
979
977
- sys.argv = [' example.py' , ' --help' ]
978
- Settings()
980
+ try :
981
+ sys.argv = [' example.py' , ' --help' ]
982
+ Settings()
983
+ except SystemExit as e:
984
+ print (e)
985
+ # > 0
979
986
"""
980
987
usage: example.py [-h] [--sub_model.v1 int]
981
988
@@ -998,7 +1005,7 @@ Alternatively, we can also configure CLI settings to pull from the class docstri
998
1005
If the field is a union of nested models the group help text will always be pulled from the field description;
999
1006
even if ` cli_use_class_docs_for_groups ` is set to ` True ` .
1000
1007
1001
- ``` py test="skip"
1008
+ ``` py
1002
1009
import sys
1003
1010
1004
1011
from pydantic import BaseModel, Field
@@ -1018,8 +1025,12 @@ class Settings(BaseSettings, cli_parse_args=True, cli_use_class_docs_for_groups=
1018
1025
sub_model: SubModel = Field(description = ' The help text from the field description' )
1019
1026
1020
1027
1021
- sys.argv = [' example.py' , ' --help' ]
1022
- Settings()
1028
+ try :
1029
+ sys.argv = [' example.py' , ' --help' ]
1030
+ Settings()
1031
+ except SystemExit as e:
1032
+ print (e)
1033
+ # > 0
1023
1034
"""
1024
1035
usage: example.py [-h] [--sub_model JSON] [--sub_model.v1 int]
1025
1036
@@ -1075,12 +1086,12 @@ command line arguments. The `CliSettingsSource` internal parser representation i
1075
1086
therefore, requires parser methods that support the same attributes as their ` argparse ` counterparts. The available
1076
1087
parser methods that can be customised, along with their argparse counterparts (the defaults), are listed below:
1077
1088
1078
- * ` parse_args_method ` - argparse.ArgumentParser.parse_args
1079
- * ` add_argument_method ` - argparse.ArgumentParser.add_argument
1080
- * ` add_argument_group_method ` - argparse.ArgumentParser.add \_ argument_group
1081
- * ` add_parser_method ` - argparse.\ _ SubParsersAction.add_parser
1082
- * ` add_subparsers_method ` - argparse.ArgumentParser.add_subparsers
1083
- * ` formatter_class ` - argparse.HelpFormatter
1089
+ * ` parse_args_method ` - ( ` argparse.ArgumentParser.parse_args ` )
1090
+ * ` add_argument_method ` - ( ` argparse.ArgumentParser.add_argument ` )
1091
+ * ` add_argument_group_method ` - ( ` argparse.ArgumentParser.add_argument_group ` )
1092
+ * ` add_parser_method ` - ( ` argparse._SubParsersAction.add_parser ` )
1093
+ * ` add_subparsers_method ` - ( ` argparse.ArgumentParser.add_subparsers ` )
1094
+ * ` formatter_class ` - ( ` argparse.HelpFormatter ` )
1084
1095
1085
1096
For a non-argparse parser the parser methods can be set to ` None ` if not supported. The CLI settings will only raise an
1086
1097
error when connecting to the root parser if a parser method is necessary but set to ` None ` .
0 commit comments