@@ -166,9 +166,12 @@ class Lazy:
166166
167167 _dynaconf_lazy_format = True
168168
169- def __init__ (self , value = empty , formatter = Formatters .python_formatter ):
169+ def __init__ (
170+ self , value = empty , formatter = Formatters .python_formatter , casting = None
171+ ):
170172 self .value = value
171173 self .formatter = formatter
174+ self .casting = casting
172175
173176 @property
174177 def context (self ):
@@ -179,7 +182,10 @@ def __call__(self, settings, validator_object=None):
179182 """LazyValue triggers format lazily."""
180183 self .settings = settings
181184 self .context ["_validator_object" ] = validator_object
182- return self .formatter (self .value , ** self .context )
185+ result = self .formatter (self .value , ** self .context )
186+ if self .casting is not None :
187+ result = self .casting (result )
188+ return result
183189
184190 def __str__ (self ):
185191 """Gives string representation for the object."""
@@ -193,6 +199,11 @@ def _dynaconf_encode(self):
193199 """Encodes this object values to be serializable to json"""
194200 return f"@{ self .formatter } { self .value } "
195201
202+ def set_casting (self , casting ):
203+ """Set the casting and return the instance."""
204+ self .casting = casting
205+ return self
206+
196207
197208def try_to_encode (value , callback = str ):
198209 """Tries to encode a value by verifying existence of `_dynaconf_encode`"""
@@ -215,11 +226,25 @@ def evaluate(settings, *args, **kwargs):
215226
216227
217228converters = {
218- "@str" : str ,
219- "@int" : int ,
220- "@float" : float ,
221- "@bool" : lambda value : str (value ).lower () in true_values ,
222- "@json" : json .loads ,
229+ "@str" : lambda value : value .set_casting (str )
230+ if isinstance (value , Lazy )
231+ else str (value ),
232+ "@int" : lambda value : value .set_casting (int )
233+ if isinstance (value , Lazy )
234+ else int (value ),
235+ "@float" : lambda value : value .set_casting (float )
236+ if isinstance (value , Lazy )
237+ else float (value ),
238+ "@bool" : lambda value : value .set_casting (
239+ lambda x : str (x ).lower () in true_values
240+ )
241+ if isinstance (value , Lazy )
242+ else str (value ).lower () in true_values ,
243+ "@json" : lambda value : value .set_casting (
244+ lambda x : json .loads (x .replace ("'" , '"' ))
245+ )
246+ if isinstance (value , Lazy )
247+ else json .loads (value ),
223248 "@format" : lambda value : Lazy (value ),
224249 "@jinja" : lambda value : Lazy (value , formatter = Formatters .jinja_formatter ),
225250 # Meta Values to trigger pre assignment actions
@@ -279,10 +304,22 @@ def _parse_conf_data(data, tomlfy=False, box_settings=None):
279304 and isinstance (data , str )
280305 and data .startswith (tuple (converters .keys ()))
281306 ):
282- parts = data .partition (" " )
283- converter_key = parts [0 ]
284- value = parts [- 1 ]
285- value = get_converter (converter_key , value , box_settings )
307+ # Check combination token is used
308+ comb_token = re .match (
309+ r"^@(str|int|float|bool|json) @(jinja|format)" , data
310+ )
311+ if comb_token :
312+ tokens = comb_token .group (0 )
313+ converter_key_list = tokens .split (" " )
314+ value = data .replace (tokens , "" ).strip ()
315+ else :
316+ parts = data .partition (" " )
317+ converter_key_list = [parts [0 ]]
318+ value = parts [- 1 ]
319+
320+ # Parse the converters iteratively
321+ for converter_key in converter_key_list [::- 1 ]:
322+ value = get_converter (converter_key , value , box_settings )
286323 else :
287324 value = parse_with_toml (data ) if tomlfy else data
288325
0 commit comments