11import logging
22import os
3+ from functools import partial
34from pprint import pformat
4- from typing import Callable , Optional , Type
5+ from typing import Any , Callable , Dict , Optional , Type
56
67import typer
7- from pydantic import ValidationError
8+ from pydantic import BaseModel , SecretStr , ValidationError
89from pydantic .env_settings import BaseSettings
10+ from pydantic .json import custom_pydantic_encoder
911
1012from ._constants import HEADER_STR
1113from .base import BaseCustomSettings
@@ -19,11 +21,20 @@ def print_as_envfile(
1921 show_secrets : bool ,
2022 ** pydantic_export_options ,
2123):
24+ exclude_unset = pydantic_export_options .get ("exclude_unset" , False )
25+
2226 for field in settings_obj .__fields__ .values ():
27+ auto_default_from_env = field .field_info .extra .get (
28+ "auto_default_from_env" , False
29+ )
2330
2431 value = getattr (settings_obj , field .name )
25- if show_secrets and hasattr (value , "get_secret_value" ):
26- value = value .get_secret_value ()
32+
33+ if exclude_unset and field .name not in settings_obj .__fields_set__ :
34+ if not auto_default_from_env :
35+ continue
36+ if value is None :
37+ continue
2738
2839 if isinstance (value , BaseSettings ):
2940 if compact :
@@ -32,9 +43,15 @@ def print_as_envfile(
3243 if verbose :
3344 typer .echo (f"\n # --- { field .name } --- " )
3445 print_as_envfile (
35- value , compact = False , verbose = verbose , show_secrets = show_secrets
46+ value ,
47+ compact = False ,
48+ verbose = verbose ,
49+ show_secrets = show_secrets ,
50+ ** pydantic_export_options ,
3651 )
3752 continue
53+ elif show_secrets and hasattr (value , "get_secret_value" ):
54+ value = value .get_secret_value ()
3855
3956 if verbose :
4057 field_info = field .field_info
@@ -50,6 +67,18 @@ def print_as_json(settings_obj, *, compact=False, **pydantic_export_options):
5067 )
5168
5269
70+ def create_json_encoder_wo_secrets (model_cls : Type [BaseModel ]):
71+ current_encoders = getattr (model_cls .Config , "json_encoders" , {})
72+ encoder = partial (
73+ custom_pydantic_encoder ,
74+ {
75+ SecretStr : lambda v : v .get_secret_value (),
76+ ** current_encoders ,
77+ },
78+ )
79+ return encoder
80+
81+
5382def create_settings_command (
5483 settings_cls : Type [BaseCustomSettings ], logger : Optional [logging .Logger ] = None
5584) -> Callable :
@@ -74,13 +103,13 @@ def settings(
74103 ),
75104 ):
76105 """Resolves settings and prints envfile"""
77- pydantic_export_options = {"exclude_unset" : exclude_unset }
78106
79107 if as_json_schema :
80108 typer .echo (settings_cls .schema_json (indent = 0 if compact else 2 ))
81109 return
82110
83111 try :
112+
84113 settings_obj = settings_cls .create_from_envs ()
85114
86115 except ValidationError as err :
@@ -110,6 +139,13 @@ def settings(
110139 )
111140 raise
112141
142+ pydantic_export_options : Dict [str , Any ] = {"exclude_unset" : exclude_unset }
143+ if show_secrets :
144+ # NOTE: this option is for json-only
145+ pydantic_export_options ["encoder" ] = create_json_encoder_wo_secrets (
146+ settings_cls
147+ )
148+
113149 if as_json :
114150 print_as_json (settings_obj , compact = compact , ** pydantic_export_options )
115151 else :
0 commit comments