def parse_cfgstr_list2(cfgstr_list, named_defaults_dict=None, cfgtype=None,
                       alias_keys=None, valid_keys=None, expand_nested=True,
                       strict=True, special_join_dict=None, is_nestedcfgtype=False,
                       metadata=None):
    r"""
    Parses config strings. By looking up name in a dict of configs

    Args:
        cfgstr_list (list):
        named_defaults_dict (dict): (default = None)
        cfgtype (None): (default = None)
        alias_keys (None): (default = None)
        valid_keys (None): (default = None)
        expand_nested (bool): (default = True)
        strict (bool): (default = True)
        is_nestedcfgtype - used for annot configs so special joins arent geometrically combined

    Note:
        Normal Case:
            --flag name

        Custom Arugment Cases:
            --flag name:custom_key1=custom_val1,custom_key2=custom_val2

        Multiple Config Case:
            --flag name1:custom_args1 name2:custom_args2

        Multiple Config (special join) Case:
            (here name2 and name3 have some special interaction)
            --flag name1:custom_args1 name2:custom_args2::name3:custom_args3

        Varied Argument Case:
            --flag name:key1=[val1,val2]

    Returns:
        list: cfg_combos_list

    CommandLine:
        python -m utool.util_gridsearch --test-parse_cfgstr_list2
        python -m utool.util_gridsearch --test-parse_cfgstr_list2:0
        python -m utool.util_gridsearch --test-parse_cfgstr_list2:1
        python -m utool.util_gridsearch --test-parse_cfgstr_list2:2

    Setup:
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> named_defaults_dict = None
        >>> cfgtype, alias_keys, valid_keys, metadata = None, None, None, None
        >>> expand_nested, is_nestedcfgtypel, strict = True, False, False

    Example0:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> named_defaults_dict = None
        >>> cfgtype, alias_keys, valid_keys, metadata = None, None, None, None
        >>> expand_nested, is_nestedcfgtypel, strict = True, False, False
        >>> cfgstr_list = ['name', 'name:f=1', 'name:b=[1,2]', 'name1:f=1::name2:f=1,b=2']
        >>> #cfgstr_list = ['name', 'name1:f=1::name2:f=1,b=2']
        >>> special_join_dict = {'joined': True}
        >>> cfg_combos_list = parse_cfgstr_list2(
        >>>     cfgstr_list, named_defaults_dict, cfgtype, alias_keys, valid_keys,
        >>>     expand_nested, strict, special_join_dict)
        >>> print('b' in cfg_combos_list[2][0])
        >>> print('cfg_combos_list = %s' % (ut.repr4(cfg_combos_list, nl=2),))
        >>> assert 'b' in cfg_combos_list[2][0], 'second cfg[2] should vary b'
        >>> assert 'b' in cfg_combos_list[2][1], 'second cfg[2] should vary b'
        >>> print(ut.depth_profile(cfg_combos_list))
        >>> cfg_list = ut.flatten(cfg_combos_list)
        >>> cfg_list = ut.flatten([cfg if isinstance(cfg, list) else [cfg] for cfg in cfg_list])
        >>> result = ut.repr2(ut.get_varied_cfg_lbls(cfg_list))
        >>> print(result)
        ['name:', 'name:f=1', 'name:b=1', 'name:b=2', 'name1:f=1,joined=True', 'name2:b=2,f=1,joined=True']

    Example1:
        >>> # ENABLE_DOCTEST
        >>> # Allow for definition of a named default on the fly
        >>> cfgstr_list = ['base=:f=2,c=[1,2]', 'base:f=1', 'base:b=[1,2]']
        >>> special_join_dict = None
        >>> cfg_combos_list = parse_cfgstr_list2(
        >>>     cfgstr_list, named_defaults_dict, cfgtype, alias_keys, valid_keys,
        >>>     expand_nested, strict, special_join_dict)
        >>> print('cfg_combos_list = %s' % (ut.repr4(cfg_combos_list, nl=2),))
        >>> print(ut.depth_profile(cfg_combos_list))
        >>> cfg_list = ut.flatten(cfg_combos_list)
        >>> cfg_list = ut.flatten([cfg if isinstance(cfg, list) else [cfg] for cfg in cfg_list])
        >>> result = ut.repr2(ut.get_varied_cfg_lbls(cfg_list))
        >>> print(result)
        ['base:c=1,f=1', 'base:c=2,f=1', 'base:b=1,c=1,f=2', 'base:b=1,c=2,f=2', 'base:b=2,c=1,f=2', 'base:b=2,c=2,f=2']

    Example2:
        >>> # ENABLE_DOCTEST
        >>> cfgstr_list = ['base:f=2,c=[(1,2),(3,4)]']
        >>> special_join_dict = None
        >>> cfg_combos_list = parse_cfgstr_list2(
        >>>     cfgstr_list, named_defaults_dict, cfgtype, alias_keys, valid_keys,
        >>>     expand_nested, strict, special_join_dict)
        >>> print('cfg_combos_list = %s' % (ut.repr4(cfg_combos_list, nl=2),))
        >>> print(ut.depth_profile(cfg_combos_list))
        >>> cfg_list = ut.flatten(cfg_combos_list)
        >>> cfg_list = ut.flatten([cfg if isinstance(cfg, list) else [cfg] for cfg in cfg_list])
        >>> result = ut.repr2(ut.get_varied_cfg_lbls(cfg_list))
        >>> print(result)

    Example3:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> named_defaults_dict = None
        >>> cfgtype, alias_keys, valid_keys, metadata = None, None, None, None
        >>> expand_nested, is_nestedcfgtypel, strict = True, False, False
        >>> # test simplest case
        >>> cfgstr_list = ['name:b=[1,2]']
        >>> special_join_dict = {'joined': True}
        >>> cfg_combos_list = parse_cfgstr_list2(
        >>>     cfgstr_list, named_defaults_dict, cfgtype, alias_keys, valid_keys,
        >>>     expand_nested, strict, special_join_dict)
        >>> print('b' in cfg_combos_list[0][0])
        >>> print('cfg_combos_list = %s' % (ut.repr4(cfg_combos_list, nl=2),))
        >>> assert 'b' in cfg_combos_list[0][0], 'second cfg[2] should vary b'
        >>> assert 'b' in cfg_combos_list[0][1], 'second cfg[2] should vary b'
        >>> print(ut.depth_profile(cfg_combos_list))
        >>> cfg_list = ut.flatten(cfg_combos_list)
        >>> cfg_list = ut.flatten([cfg if isinstance(cfg, list) else [cfg] for cfg in cfg_list])
        >>> result = ut.repr2(ut.get_varied_cfg_lbls(cfg_list))
        >>> print(result)
    """
    import utool as ut
    #with ut.Indenter('    '):
    cfg_combos_list = []
    cfgstr_list_ = []

    # special named defaults assignment
    dyndef_named_defaults = {}
    for cfgstr in cfgstr_list:
        if cfgstr.find('=:') > -1:
            cfgname, cfgopt_strs, subx = parse_cfgstr_name_options(cfgstr)
            assert cfgname.endswith('=')
            cfgname = cfgname[:-1]
            base_cfg_list = lookup_base_cfg_list(cfgname,
                                                 named_defaults_dict,
                                                 metadata=metadata)
            cfg_options = noexpand_parse_cfgstrs(cfgopt_strs)
            dyndef_named_defaults[cfgname] = cfg_options
        else:
            cfgstr_list_.append(cfgstr)
    if len(dyndef_named_defaults) > 0 and named_defaults_dict is None:
        named_defaults_dict = dyndef_named_defaults

    for cfgstr in cfgstr_list_:
        cfg_combos = []
        # Parse special joined cfg case
        if cfgstr.find('::') > -1:
            special_cfgstr_list = cfgstr.split('::')
            # Recursive call
            special_combo_list = parse_cfgstr_list2(
                special_cfgstr_list,
                named_defaults_dict=named_defaults_dict, cfgtype=cfgtype,
                alias_keys=alias_keys, valid_keys=valid_keys,
                strict=strict, expand_nested=expand_nested,
                is_nestedcfgtype=False, metadata=metadata)
            if special_join_dict is not None:
                for special_combo in special_combo_list:
                    for cfg in special_combo:
                        cfg.update(special_join_dict)
            if is_nestedcfgtype:
                cfg_combo = tuple([combo for combo in special_combo_list])
            else:
                # not sure if this is right
                cfg_combo = special_combo_list
            # FIXME DUPLICATE CODE
            if expand_nested:
                cfg_combos.extend(cfg_combo)
            else:
                #print('Appending: ' + str(ut.depth_profile(cfg_combo)))
                #if ut.depth_profile(cfg_combo) == [1, 9]:
                #    ut.embed()
                cfg_combos_list.append(cfg_combo)
        else:
            # Normal Case
            cfgname, cfgopt_strs, subx = parse_cfgstr_name_options(cfgstr)
            # --
            # Lookup named default settings
            try:
                base_cfg_list = lookup_base_cfg_list(cfgname,
                                                     named_defaults_dict,
                                                     metadata=metadata)
            except Exception as ex:
                ut.printex(ex, keys=['cfgstr_list', 'cfgstr_list_'])
                raise
            # --
            for base_cfg in base_cfg_list:
                print('cfgname = %r' % (cfgname,))
                print('cfgopt_strs = %r' % (cfgopt_strs,))
                print('base_cfg = %r' % (base_cfg,))
                print('alias_keys = %r' % (alias_keys,))
                print('cfgtype = %r' % (cfgtype,))
                print('offset = %r' % (len(cfg_combos),))
                print('valid_keys = %r' % (valid_keys,))
                print('strict = %r' % (strict,))
                cfg_combo = customize_base_cfg(
                    cfgname, cfgopt_strs, base_cfg, cfgtype, alias_keys,
                    valid_keys, strict=strict, offset=len(cfg_combos))
                if is_nestedcfgtype:
                    cfg_combo = [cfg_combo]
                if expand_nested:
                    cfg_combos.extend(cfg_combo)
                else:
                    cfg_combos_list.append(cfg_combo)
        # SUBX Cannot work here because of acfg hackiness
        #if subx is not None:
        #    cfg_combo = ut.take(cfg_combo, subx)
        if expand_nested:
            cfg_combos_list.append(cfg_combos)
    #    print('Updated to: ' + str(ut.depth_profile(cfg_combos_list)))
    #print('Returning len(cfg_combos_list) = %r' % (len(cfg_combos_list),))
    return cfg_combos_list
def parse_cfgstr_list2 ( cfgstr_list , named_defaults_dict = None , cfgtype = None , alias_keys = None , valid_keys = None , expand_nested = True , strict = True , special_join_dict = None , is_nestedcfgtype = False , metadata = None ) : import utool as ut cfg_combos_list = [ ] cfgstr_list_ = [ ] dyndef_named_defaults = { } for cfgstr in cfgstr_list : if cfgstr . find ( '=:' ) > - 1 : cfgname , cfgopt_strs , subx = parse_cfgstr_name_options ( cfgstr ) assert cfgname . endswith ( '=' ) cfgname = cfgname [ : - 1 ] base_cfg_list = lookup_base_cfg_list ( cfgname , named_defaults_dict , metadata = metadata ) cfg_options = noexpand_parse_cfgstrs ( cfgopt_strs ) dyndef_named_defaults [ cfgname ] = cfg_options else : cfgstr_list_ . append ( cfgstr ) if len ( dyndef_named_defaults ) > 0 and named_defaults_dict is None : named_defaults_dict = dyndef_named_defaults for cfgstr in cfgstr_list_ : cfg_combos = [ ] if cfgstr . find ( '::' ) > - 1 : special_cfgstr_list = cfgstr . split ( '::' ) special_combo_list = parse_cfgstr_list2 ( special_cfgstr_list , named_defaults_dict = named_defaults_dict , cfgtype = cfgtype , alias_keys = alias_keys , valid_keys = valid_keys , strict = strict , expand_nested = expand_nested , is_nestedcfgtype = False , metadata = metadata ) if special_join_dict is not None : for special_combo in special_combo_list : for cfg in special_combo : cfg . update ( special_join_dict ) if is_nestedcfgtype : cfg_combo = tuple ( [ combo for combo in special_combo_list ] ) else : cfg_combo = special_combo_list if expand_nested : cfg_combos . extend ( cfg_combo ) else : cfg_combos_list . append ( cfg_combo ) else : cfgname , cfgopt_strs , subx = parse_cfgstr_name_options ( cfgstr ) try : base_cfg_list = lookup_base_cfg_list ( cfgname , named_defaults_dict , metadata = metadata ) except Exception as ex : ut . printex ( ex , keys = [ 'cfgstr_list' , 'cfgstr_list_' ] ) raise for base_cfg in base_cfg_list : print ( 'cfgname = %r' % ( cfgname , ) ) print ( 'cfgopt_strs = %r' % ( cfgopt_strs , ) ) print ( 'base_cfg = %r' % ( base_cfg , ) ) print ( 'alias_keys = %r' % ( alias_keys , ) ) print ( 'cfgtype = %r' % ( cfgtype , ) ) print ( 'offset = %r' % ( len ( cfg_combos ) , ) ) print ( 'valid_keys = %r' % ( valid_keys , ) ) print ( 'strict = %r' % ( strict , ) ) cfg_combo = customize_base_cfg ( cfgname , cfgopt_strs , base_cfg , cfgtype , alias_keys , valid_keys , strict = strict , offset = len ( cfg_combos ) ) if is_nestedcfgtype : cfg_combo = [ cfg_combo ] if expand_nested : cfg_combos . extend ( cfg_combo ) else : cfg_combos_list . append ( cfg_combo ) if expand_nested : cfg_combos_list . append ( cfg_combos ) return cfg_combos_list
r"""
    Parses config strings. By looking up name in a dict of configs

    Args:
        cfgstr_list (list):
        named_defaults_dict (dict): (default = None)
        cfgtype (None): (default = None)
        alias_keys (None): (default = None)
        valid_keys (None): (default = None)
        expand_nested (bool): (default = True)
        strict (bool): (default = True)
        is_nestedcfgtype - used for annot configs so special joins arent geometrically combined

    Note:
        Normal Case:
            --flag name

        Custom Arugment Cases:
            --flag name:custom_key1=custom_val1,custom_key2=custom_val2

        Multiple Config Case:
            --flag name1:custom_args1 name2:custom_args2

        Multiple Config (special join) Case:
            (here name2 and name3 have some special interaction)
            --flag name1:custom_args1 name2:custom_args2::name3:custom_args3

        Varied Argument Case:
            --flag name:key1=[val1,val2]

    Returns:
        list: cfg_combos_list

    CommandLine:
        python -m utool.util_gridsearch --test-parse_cfgstr_list2
        python -m utool.util_gridsearch --test-parse_cfgstr_list2:0
        python -m utool.util_gridsearch --test-parse_cfgstr_list2:1
        python -m utool.util_gridsearch --test-parse_cfgstr_list2:2

    Setup:
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> named_defaults_dict = None
        >>> cfgtype, alias_keys, valid_keys, metadata = None, None, None, None
        >>> expand_nested, is_nestedcfgtypel, strict = True, False, False

    Example0:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> named_defaults_dict = None
        >>> cfgtype, alias_keys, valid_keys, metadata = None, None, None, None
        >>> expand_nested, is_nestedcfgtypel, strict = True, False, False
        >>> cfgstr_list = ['name', 'name:f=1', 'name:b=[1,2]', 'name1:f=1::name2:f=1,b=2']
        >>> #cfgstr_list = ['name', 'name1:f=1::name2:f=1,b=2']
        >>> special_join_dict = {'joined': True}
        >>> cfg_combos_list = parse_cfgstr_list2(
        >>>     cfgstr_list, named_defaults_dict, cfgtype, alias_keys, valid_keys,
        >>>     expand_nested, strict, special_join_dict)
        >>> print('b' in cfg_combos_list[2][0])
        >>> print('cfg_combos_list = %s' % (ut.repr4(cfg_combos_list, nl=2),))
        >>> assert 'b' in cfg_combos_list[2][0], 'second cfg[2] should vary b'
        >>> assert 'b' in cfg_combos_list[2][1], 'second cfg[2] should vary b'
        >>> print(ut.depth_profile(cfg_combos_list))
        >>> cfg_list = ut.flatten(cfg_combos_list)
        >>> cfg_list = ut.flatten([cfg if isinstance(cfg, list) else [cfg] for cfg in cfg_list])
        >>> result = ut.repr2(ut.get_varied_cfg_lbls(cfg_list))
        >>> print(result)
        ['name:', 'name:f=1', 'name:b=1', 'name:b=2', 'name1:f=1,joined=True', 'name2:b=2,f=1,joined=True']

    Example1:
        >>> # ENABLE_DOCTEST
        >>> # Allow for definition of a named default on the fly
        >>> cfgstr_list = ['base=:f=2,c=[1,2]', 'base:f=1', 'base:b=[1,2]']
        >>> special_join_dict = None
        >>> cfg_combos_list = parse_cfgstr_list2(
        >>>     cfgstr_list, named_defaults_dict, cfgtype, alias_keys, valid_keys,
        >>>     expand_nested, strict, special_join_dict)
        >>> print('cfg_combos_list = %s' % (ut.repr4(cfg_combos_list, nl=2),))
        >>> print(ut.depth_profile(cfg_combos_list))
        >>> cfg_list = ut.flatten(cfg_combos_list)
        >>> cfg_list = ut.flatten([cfg if isinstance(cfg, list) else [cfg] for cfg in cfg_list])
        >>> result = ut.repr2(ut.get_varied_cfg_lbls(cfg_list))
        >>> print(result)
        ['base:c=1,f=1', 'base:c=2,f=1', 'base:b=1,c=1,f=2', 'base:b=1,c=2,f=2', 'base:b=2,c=1,f=2', 'base:b=2,c=2,f=2']

    Example2:
        >>> # ENABLE_DOCTEST
        >>> cfgstr_list = ['base:f=2,c=[(1,2),(3,4)]']
        >>> special_join_dict = None
        >>> cfg_combos_list = parse_cfgstr_list2(
        >>>     cfgstr_list, named_defaults_dict, cfgtype, alias_keys, valid_keys,
        >>>     expand_nested, strict, special_join_dict)
        >>> print('cfg_combos_list = %s' % (ut.repr4(cfg_combos_list, nl=2),))
        >>> print(ut.depth_profile(cfg_combos_list))
        >>> cfg_list = ut.flatten(cfg_combos_list)
        >>> cfg_list = ut.flatten([cfg if isinstance(cfg, list) else [cfg] for cfg in cfg_list])
        >>> result = ut.repr2(ut.get_varied_cfg_lbls(cfg_list))
        >>> print(result)

    Example3:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_gridsearch import *  # NOQA
        >>> import utool as ut
        >>> named_defaults_dict = None
        >>> cfgtype, alias_keys, valid_keys, metadata = None, None, None, None
        >>> expand_nested, is_nestedcfgtypel, strict = True, False, False
        >>> # test simplest case
        >>> cfgstr_list = ['name:b=[1,2]']
        >>> special_join_dict = {'joined': True}
        >>> cfg_combos_list = parse_cfgstr_list2(
        >>>     cfgstr_list, named_defaults_dict, cfgtype, alias_keys, valid_keys,
        >>>     expand_nested, strict, special_join_dict)
        >>> print('b' in cfg_combos_list[0][0])
        >>> print('cfg_combos_list = %s' % (ut.repr4(cfg_combos_list, nl=2),))
        >>> assert 'b' in cfg_combos_list[0][0], 'second cfg[2] should vary b'
        >>> assert 'b' in cfg_combos_list[0][1], 'second cfg[2] should vary b'
        >>> print(ut.depth_profile(cfg_combos_list))
        >>> cfg_list = ut.flatten(cfg_combos_list)
        >>> cfg_list = ut.flatten([cfg if isinstance(cfg, list) else [cfg] for cfg in cfg_list])
        >>> result = ut.repr2(ut.get_varied_cfg_lbls(cfg_list))
        >>> print(result)