11import importlib
22import json
33import os
4-
4+ from collections . abc import Mapping
55from typing import Any , Callable , Dict , List , Optional , Tuple , Union
66
77from metaflow ._vendor import click
@@ -157,7 +157,7 @@ def make_key_name(name: str) -> str:
157157 # Special mark to indicate that the configuration value is not content or a file
158158 # name but a value that should be read in the config file (effectively where
159159 # the value has already been materialized).
160- return "kv." + name . lower ()
160+ return "kv." + name
161161
162162 @classmethod
163163 def set_config_file (cls , config_file : str ):
@@ -225,13 +225,13 @@ def process_configs(
225225 # and is clearer
226226 if param_name == "config_value" :
227227 self ._value_values = {
228- k . lower () : v
228+ k : v
229229 for k , v in param_value .items ()
230230 if v is not None and not v .startswith (_CONVERTED_DEFAULT )
231231 }
232232 else :
233233 self ._path_values = {
234- k . lower () : v
234+ k : v
235235 for k , v in param_value .items ()
236236 if v is not None and not v .startswith (_CONVERTED_DEFAULT )
237237 }
@@ -286,7 +286,7 @@ def process_configs(
286286 merged_configs = {}
287287 # Now look at everything (including defaults)
288288 for name , (val , is_path ) in self ._defaults .items ():
289- n = name . lower ()
289+ n = name
290290 if n in all_values :
291291 # We have the value provided by the user -- use that.
292292 merged_configs [n ] = all_values [n ]
@@ -331,7 +331,10 @@ def process_configs(
331331 if val is None :
332332 missing_configs .add (name )
333333 to_return [name ] = None
334- flow_cls ._flow_state .self_data [FlowStateItems .CONFIGS ][name ] = None
334+ flow_cls ._flow_state .self_data [FlowStateItems .CONFIGS ][name ] = (
335+ None ,
336+ True ,
337+ )
335338 continue
336339 if val .startswith (_CONVERTED_NO_FILE ):
337340 no_file .append (name )
@@ -340,13 +343,14 @@ def process_configs(
340343 no_default_file .append (name )
341344 continue
342345
346+ parser , is_plain = self ._parsers [name ]
343347 val = val [len (_CONVERT_PREFIX ) :] # Remove the _CONVERT_PREFIX
344348 if val .startswith (_DEFAULT_PREFIX ): # Remove the _DEFAULT_PREFIX if needed
345349 val = val [len (_DEFAULT_PREFIX ) :]
346350 if val .startswith ("kv." ):
347351 # This means to load it from a file
348352 try :
349- read_value = self .get_config (val [3 :])
353+ read_value , read_is_plain = self .get_config (val [3 :])
350354 except KeyError as e :
351355 exc = click .UsageError (
352356 "Could not find configuration '%s' in INFO file" % val
@@ -355,15 +359,23 @@ def process_configs(
355359 click_obj .delayed_config_exception = exc
356360 return None
357361 raise exc from e
358- flow_cls ._flow_state .self_data [FlowStateItems .CONFIGS ][
359- name
360- ] = read_value
362+ if read_is_plain != is_plain :
363+ raise click .UsageError (
364+ "Configuration '%s' mismatched `plain` attribute -- "
365+ "this is a bug, please report it." % val [3 :]
366+ )
367+ flow_cls ._flow_state .self_data [FlowStateItems .CONFIGS ][name ] = (
368+ read_value ,
369+ True if read_value is None else is_plain ,
370+ )
361371 to_return [name ] = (
362- ConfigValue (read_value ) if read_value is not None else None
372+ read_value
373+ if read_value is None or is_plain
374+ else ConfigValue (read_value )
363375 )
364376 else :
365- if self . _parsers [ name ] :
366- read_value = self ._call_parser (self . _parsers [ name ] , val )
377+ if parser :
378+ read_value = self ._call_parser (parser , val , is_plain )
367379 else :
368380 try :
369381 read_value = json .loads (val )
@@ -374,11 +386,14 @@ def process_configs(
374386 )
375387 continue
376388 # TODO: Support YAML
377- flow_cls ._flow_state .self_data [FlowStateItems .CONFIGS ][
378- name
379- ] = read_value
389+ flow_cls ._flow_state .self_data [FlowStateItems .CONFIGS ][name ] = (
390+ read_value ,
391+ True if read_value is None else is_plain ,
392+ )
380393 to_return [name ] = (
381- ConfigValue (read_value ) if read_value is not None else None
394+ read_value
395+ if read_value is None or is_plain
396+ else ConfigValue (read_value )
382397 )
383398
384399 reqs = missing_configs .intersection (self ._req_configs )
@@ -423,7 +438,7 @@ def __repr__(self):
423438 return "ConfigInput"
424439
425440 @staticmethod
426- def _call_parser (parser , val ):
441+ def _call_parser (parser , val , is_plain ):
427442 if isinstance (parser , str ):
428443 if len (parser ) and parser [0 ] == "." :
429444 parser = "metaflow" + parser
@@ -438,7 +453,13 @@ def _call_parser(parser, val):
438453 "Parser %s is either not part of %s or not a callable"
439454 % (func , path )
440455 )
441- return parser (val )
456+ return_value = parser (val )
457+ if not is_plain and not isinstance (return_value , Mapping ):
458+ raise ValueError (
459+ "Parser %s returned a value that is not a mapping (got type %s): %s"
460+ % (str (parser ), type (return_value ), return_value )
461+ )
462+ return return_value
442463
443464
444465class LocalFileInput (click .Path ):
@@ -474,23 +495,22 @@ def config_options_with_config_input(cmd):
474495 # List all the configuration options
475496 for arg in parameters [::- 1 ]:
476497 kwargs = arg .option_kwargs (False )
477- if arg .name . lower () in config_seen :
498+ if arg .name in config_seen :
478499 msg = (
479- "Multiple configurations use the same name '%s'. Note that names are "
480- "case-insensitive. Please change the "
500+ "Multiple configurations use the same name '%s'. Please change the "
481501 "names of some of your configurations" % arg .name
482502 )
483503 raise MetaflowException (msg )
484- config_seen .add (arg .name . lower () )
504+ config_seen .add (arg .name )
485505 if kwargs ["required" ]:
486506 required_names .append (arg .name )
487507
488- defaults [arg .name . lower () ] = (
508+ defaults [arg .name ] = (
489509 arg .kwargs .get ("default" , None ),
490510 arg ._default_is_file ,
491511 )
492- help_strs .append (" - %s: %s" % (arg .name . lower () , kwargs .get ("help" , "" )))
493- parsers [arg .name . lower () ] = arg .parser
512+ help_strs .append (" - %s: %s" % (arg .name , kwargs .get ("help" , "" )))
513+ parsers [arg .name ] = ( arg .parser , arg . kwargs [ "plain" ])
494514
495515 if not config_seen :
496516 # No configurations -- don't add anything; we set it to False so that it
0 commit comments