def recurse(level, name, types, values, delimeter="-"): """ Recurse through a flattened ``form`` dictionary, reconstructing dictionary to match structure specified by ``spec``. :param dict spec: the canonical_args argspec dict :param dict form: the flat HTML form data :param str delimeter: default ``"-"``, the string character used to separate levels in the ``form.keys()`` entries. :returns: ``dict``, the reconstructed, type correct argument dictionary, matching the ``spec`` structure. """ subtype = check.eval_subtype(types) # choice of one if isinstance(subtype, check.ChoiceOfOne): entry = NotSpecified for index, subsubtype in enumerate(subtype): try: entry = recurse(level + 1, name, subsubtype, values[check.type_to_string(subsubtype)]) except KeyError as e: pass else: break if entry is NotSpecified: # we couldnt find a valid entry # See? Told you we re-raise the key error. warnings.warn( "could not find valid entry for '{}'".format(name), RuntimeWarning) return entry # structlist elif isinstance(subtype, list) and isinstance(values, list): entry = [] for index, subsubtype in enumerate(subtype): ret = recurse(level + 1, name + "[{}]".format(index), subtype[index], values[index]) if ret is not NotSpecified: # do not append an unspecified value entry.append(ret) else: # because this is a structured list, the positional # argument is guaranteed. an unspecified value must # therefore be None. entry.append(None) return entry # structdict elif subtype == dict and isinstance(values, dict): entry = {} for kw in sorted(values): arg = values[kw] ret = recurse(level + 1, name + delimeter + kw, arg["type"], arg["values"], delimeter=delimeter) if ret is not NotSpecified: # do not set an unspecified value entry[kw] = ret else: # this is a Structured Dict, so we have to guarantee # the presence of all keys. Thusly, set None when a # value is unspecified. entry[kw] = None return entry # unstructlist elif subtype == list and values is None: # find all keys matching (other than index) pattern = re.compile('{}\[(.*)\]'.format(name)) matchingkeys = [key for key in form.keys()\ if pattern.match(key)] construct = [] for subkey in sorted(matchingkeys): index = int(re.findall('\[(.*)\]', subkey)[0]) raw = form[subkey] ret = cast(raw[0], raw[1], name=name) if ret is not NotSpecified: # ensure not to appaned a NotSpecified value construct.append(ret) # this is an unstructured list, so if a value is # unspecified, ignore the shit out of it. if len(construct) > 0: return construct else: return NotSpecified # unstructdict elif subtype == dict and values is None: pattern = re.compile('{}\[(.*)\]'.format(name)) matchingkeys = [key for key in form.keys()\ if pattern.match(key)] construct = {} for subkey in matchingkeys: raw = form[subkey] if str(raw[0]) != '': # ensure there is a valid key ret = cast(raw[1], raw[2], name=name) if ret != NotSpecified: # ensure there is a valid value construct[str(raw[0])] = ret # this is an unstructured dict, so keys are not # guaranteed here. If a value is unspecified, # simply don't append it (aka, ignore it). if len(construct) > 0: return construct else: return NotSpecified # cls elif check.type_to_string(subtype) in sources.SOURCES: identifier = form[name][0] import_string = form[name][1] obj = sources.get_one(import_string, identifier, raw=True) return obj # native else: raw = form[name] ret = cast(raw[0], raw[1], name=name) return ret
def test_type_none(self): self.assertEqual(check.eval_subtype("NoneType"), type(None))
def test_complex_type_with_none(self): self.assertEqual(check.eval_subtype("[int, str, NoneType]"), [int, str, type(None)])
def test_type_choice(self): self.assertEqual(check.eval_subtype("one([int, float])"), check.ChoiceOfOne([int, float]))
def test_choice_of_one_with_object(self): self.assertEqual( check.eval_subtype("one([int, cls('test_check.TestObj')])"), check.ChoiceOfOne([int, TestObj]))
def test_type_object(self): self.assertEqual(check.eval_subtype("test_check.TestObj"), TestObj)
def test_type_dict(self): self.assertEqual(check.eval_subtype("dict({'thing1': int})"), {"thing1": int})
def test_type_list(self): self.assertEqual(check.eval_subtype("list([int, str, float])"), [int, str, float])
def test_type_native(self): self.assertEqual(check.eval_subtype("str"), str)
def recurse(level, name, types, values, delimeter=delimeter): subtype = check.eval_subtype(types) # choice of one if isinstance(subtype, check.ChoiceOfOne): entries = [] for index, subsubtype in enumerate(subtype): entries.append( recurse(level + 1, name, subtype[index], values[check.type_to_string(subsubtype)])) template = env.get_template("one_selector.html") html = template.render(name=name, options=[check.type_to_string(x)\ for x in subtype], entries=entries) if level > 0: template = env.get_template("level.html") html = template.render(name=name.split(delimeter)[-1], include_header=True, inner=html) return html # structlist elif isinstance(subtype, list) and isinstance(values, list): # recurse html = "" for index, subsubtype in enumerate(subtype): html += recurse(level + 1, name + "[" + str(index) + "]", subtype[index], values[index]) if level > 0: template = env.get_template("level.html") html = template.render(name=name.split(delimeter)[-1], include_header=True, inner=html) return html # structdict elif subtype == dict and isinstance(values, dict): html = "" for kw in sorted(values): arg = values[kw] html += recurse(level + 1, name + delimeter + kw, arg["type"], arg["values"]) if level > 0: template = env.get_template("level.html") html = template.render(name=name.split(delimeter)[-1], include_header=True, inner=html) return html # unstructlist elif subtype == list and values is None: template = env.get_template("base.html") html = template.render(name=name, displayname=name.split(delimeter)[-1], type=check.type_to_string(subtype), inputtype="unstructlist") if level > 0: template = env.get_template("level.html") html = template.render(name=name.split(delimeter)[-1], include_header=True, inner=html) return html # unstructdict elif subtype == dict and values is None: template = env.get_template("base.html") html = template.render(name=name, displayname=name.split(delimeter)[-1], type=check.type_to_string(subtype), inputtype="unstructdict") if level > 0: template = env.get_template("level.html") html = template.render(name=name.split(delimeter)[-1], include_header=True, inner=html) return html # selector elif isinstance(values, list): template = env.get_template("base.html") return template.render(name=name, displayname=name.split(delimeter)[-1], type=check.type_to_string(subtype), options=values, inputtype="selector") # cls elif check.type_to_string(subtype) in sources.SOURCES: ids, values = sources.get_all(subtype) template = env.get_template("base.html") return template.render(name=name, displayname=name.split(delimeter)[-1], type=check.type_to_string(subtype), option_ids=ids, options=values, inputtype="selector") # native else: # name, type, inputtype template = env.get_template("base.html") return template.render(name=name, displayname=name.split(delimeter)[-1], type=check.type_to_string(subtype), constraint=values, inputtype="native")