@@ -935,27 +935,30 @@ def to_toml(self) -> str:
935935 """Export settings as TOML string."""
936936 lines = ["# PyWry Configuration" , "# Generated by: pywry config --toml" , "" ]
937937
938- sections = [
939- ( "csp" , self . csp ) ,
940- ( "theme" , self . theme ) ,
941- ( "timeout" , self . timeout ) ,
942- ( "asset" , self . asset ) ,
943- ( "log" , self . log ) ,
944- ( "window" , self . window ) ,
945- ( "hot_reload" , self . hot_reload ) ,
946- ( "server" , self . server ) ,
947- ( "deploy" , self . deploy ) ,
948- ( "mcp" , self . mcp ) ,
938+ section_names = [
939+ "csp" ,
940+ "theme" ,
941+ "timeout" ,
942+ "asset" ,
943+ "log" ,
944+ "window" ,
945+ "hot_reload" ,
946+ "server" ,
947+ "deploy" ,
948+ "mcp" ,
949949 ]
950950
951951 if self .oauth2 is not None :
952- sections .append (( "oauth2" , self . oauth2 ) )
952+ section_names .append ("oauth2" )
953953
954- for section_name , section in sections :
954+ all_data = self .model_dump (
955+ exclude = dict .fromkeys (section_names , _SENSITIVE_FIELDS ),
956+ )
957+
958+ for section_name in section_names :
959+ section_data = all_data .get (section_name , {})
955960 lines .append (f"[{ section_name } ]" )
956- # Exclude sensitive fields from model_dump so their values
957- # never enter the output string (satisfies CodeQL taint analysis).
958- for field_name , field_value in section .model_dump (exclude = _SENSITIVE_FIELDS ).items ():
961+ for field_name , field_value in section_data .items ():
959962 if isinstance (field_value , list ):
960963 value_str = "[" + ", " .join (f'"{ v } "' for v in field_value ) + "]"
961964 elif isinstance (field_value , bool ):
@@ -965,10 +968,12 @@ def to_toml(self) -> str:
965968 else :
966969 value_str = str (field_value )
967970 lines .append (f"{ field_name } = { value_str } " )
968- # Append redacted placeholders for any sensitive fields on this model.
971+ # Append redacted placeholders for any sensitive fields defined
972+ # on this section's model class.
973+ section_cls = type (getattr (self , section_name ))
969974 lines .extend (
970975 f'{ rn } = "{ _REDACTED } "'
971- for rn in sorted (_SENSITIVE_FIELDS & section .model_fields .keys ())
976+ for rn in sorted (_SENSITIVE_FIELDS & section_cls .model_fields .keys ())
972977 )
973978 lines .append ("" )
974979
@@ -982,31 +987,38 @@ def to_env(self) -> str:
982987 "" ,
983988 ]
984989
985- sections = [
986- ("CSP" , self . csp ),
987- ("THEME" , self . theme ),
988- ("TIMEOUT" , self . timeout ),
989- ("ASSET" , self . asset ),
990- ("LOG" , self . log ),
991- ("WINDOW" , self . window ),
992- ("HOT_RELOAD" , self . hot_reload ),
993- ("SERVER" , self . server ),
994- ("DEPLOY" , self . deploy ),
995- ("MCP" , self . mcp ),
990+ env_sections = [
991+ ("CSP" , " csp" ),
992+ ("THEME" , " theme" ),
993+ ("TIMEOUT" , " timeout" ),
994+ ("ASSET" , " asset" ),
995+ ("LOG" , " log" ),
996+ ("WINDOW" , " window" ),
997+ ("HOT_RELOAD" , " hot_reload" ),
998+ ("SERVER" , " server" ),
999+ ("DEPLOY" , " deploy" ),
1000+ ("MCP" , " mcp" ),
9961001 ]
9971002
998- for section_name , section in sections :
999- for field_name , field_value in section .model_dump (exclude = _SENSITIVE_FIELDS ).items ():
1000- env_name = f"PYWRY_{ section_name } __{ field_name .upper ()} "
1003+ # Dump from top-level model to avoid tainted section instances.
1004+ all_data = self .model_dump (
1005+ exclude = {attr : _SENSITIVE_FIELDS for _ , attr in env_sections },
1006+ )
1007+
1008+ for env_prefix , attr_name in env_sections :
1009+ section_data = all_data .get (attr_name , {})
1010+ for field_name , field_value in section_data .items ():
1011+ env_name = f"PYWRY_{ env_prefix } __{ field_name .upper ()} "
10011012 if isinstance (field_value , list ):
10021013 value_str = "," .join (str (v ) for v in field_value )
10031014 elif isinstance (field_value , bool ):
10041015 value_str = "true" if field_value else "false"
10051016 else :
10061017 value_str = str (field_value )
10071018 lines .append (f'export { env_name } ="{ value_str } "' )
1008- for redacted_name in sorted (_SENSITIVE_FIELDS & section .model_fields .keys ()):
1009- env_name = f"PYWRY_{ section_name } __{ redacted_name .upper ()} "
1019+ section_cls = type (getattr (self , attr_name ))
1020+ for redacted_name in sorted (_SENSITIVE_FIELDS & section_cls .model_fields .keys ()):
1021+ env_name = f"PYWRY_{ env_prefix } __{ redacted_name .upper ()} "
10101022 lines .append (f'export { env_name } ="{ _REDACTED } "' )
10111023
10121024 return "\n " .join (lines )
@@ -1015,31 +1027,38 @@ def show(self) -> str:
10151027 """Format settings as a readable table."""
10161028 lines = ["PyWry Configuration" , "=" * 60 , "" ]
10171029
1018- sections = [
1019- ("Security (CSP)" , self . csp ),
1020- ("Theme" , self . theme ),
1021- ("Timeouts" , self . timeout ),
1022- ("Assets" , self . asset ),
1023- ("Logging" , self . log ),
1024- ("Window Defaults" , self . window ),
1025- ("Hot Reload" , self . hot_reload ),
1026- ("Server (Notebook/Web)" , self . server ),
1027- ("Deploy (Scalable)" , self . deploy ),
1028- ("MCP (AI Agents)" , self . mcp ),
1030+ show_sections = [
1031+ ("Security (CSP)" , " csp" ),
1032+ ("Theme" , " theme" ),
1033+ ("Timeouts" , " timeout" ),
1034+ ("Assets" , " asset" ),
1035+ ("Logging" , " log" ),
1036+ ("Window Defaults" , " window" ),
1037+ ("Hot Reload" , " hot_reload" ),
1038+ ("Server (Notebook/Web)" , " server" ),
1039+ ("Deploy (Scalable)" , " deploy" ),
1040+ ("MCP (AI Agents)" , " mcp" ),
10291041 ]
10301042
1031- for section_name , section in sections :
1032- lines .append (f"\n { section_name } " )
1043+ # Dump from top-level model to avoid tainted section instances.
1044+ all_data = self .model_dump (
1045+ exclude = {attr : _SENSITIVE_FIELDS for _ , attr in show_sections },
1046+ )
1047+
1048+ for display_name , attr_name in show_sections :
1049+ section_data = all_data .get (attr_name , {})
1050+ lines .append (f"\n { display_name } " )
10331051 lines .append ("-" * 40 )
1034- for field_name , field_value in section . model_dump ( exclude = _SENSITIVE_FIELDS ) .items ():
1052+ for field_name , field_value in section_data .items ():
10351053 # Truncate long values
10361054 value_str = str (field_value )
10371055 if len (value_str ) > 50 :
10381056 value_str = value_str [:47 ] + "..."
10391057 lines .append (f" { field_name :20} = { value_str } " )
1058+ section_cls = type (getattr (self , attr_name ))
10401059 lines .extend (
10411060 f" { rn :20} = { _REDACTED } "
1042- for rn in sorted (_SENSITIVE_FIELDS & section .model_fields .keys ())
1061+ for rn in sorted (_SENSITIVE_FIELDS & section_cls .model_fields .keys ())
10431062 )
10441063
10451064 return "\n " .join (lines )
0 commit comments