@@ -349,30 +349,42 @@ def _preprocess_env_value(cls, env_value: str, field_type: type) -> Any: # noqa
349349
350350 return env_value
351351
352- # Handle bool
352+ # Fast path: Direct type comparison (avoid get_origin when possible)
353+ if field_type is str :
354+ return env_value
353355 if field_type is bool :
354356 return env_value .lower () in ("true" , "1" , "yes" , "y" , "t" )
355-
356- # Handle int
357357 if field_type is int :
358358 try :
359359 return int (env_value )
360360 except ValueError as e :
361361 raise ValueError (f"Cannot convert '{ env_value } ' to int" ) from e
362-
363- # Handle float
364362 if field_type is float :
365363 try :
366364 return float (env_value )
367365 except ValueError as e :
368366 raise ValueError (f"Cannot convert '{ env_value } ' to float" ) from e
369367
370- # Handle JSON types (list, dict, nested structs)
371- if env_value .startswith (("{" , "[" )):
372- try :
373- return msgspec .json .decode (env_value .encode ())
374- except msgspec .DecodeError as e :
375- raise ValueError (f"Invalid JSON in env var: { e } " ) from e
368+ # Only use typing introspection for complex types (Union, Optional, etc.)
369+ origin = get_origin (field_type )
370+ if origin is Union :
371+ args = get_args (field_type )
372+ non_none = [a for a in args if a is not type (None )]
373+ if non_none :
374+ # Cache the resolved type for future use
375+ resolved_type = non_none [0 ]
376+ cls ._type_cache [field_type ] = resolved_type
377+ # Recursively process with the non-None type
378+ return cls ._preprocess_env_value (env_value , resolved_type )
379+
380+ return env_value
381+
382+ # Type conversion (required for JSON encoding)
383+ if field_type is bool :
384+ return env_value .lower () in ("true" , "1" , "yes" , "y" , "t" )
385+ if field_type is int :
386+ return int (env_value )
387+ if field_type is float :
388+ return float (env_value )
376389
377- # Default: return as string
378390 return env_value
0 commit comments