def __init__(self, fromtuple=None, **kwargs): super().__init__(**kwargs) self._fromtuple = fromtuple if fromtuple: for value in fromtuple[0]: self.append(value) for key, value in fromtuple[1].items(): if is_container(value): start = len(self) for subvalue in value: self.append(subvalue) self.set_name(key, start, len(self)) else: self.append(value) self.add_name(key)
def __init__(self, fromtuple=None, **kwargs): """""" # blank out docstring in super class w different formatting super().__init__(**kwargs) self._fromtuple = fromtuple if fromtuple: for value in fromtuple[0]: self.append(value) for key, value in fromtuple[1].items(): if is_container(value): start = len(self) for subvalue in value: self.append(subvalue) self._set_name(key, start, len(self)) else: self.append(value) self._add_name(key)
def update_tuple(self, totuple): """Update values in `(args, kwargs)` tuple. The tuple must be the same as used in the constructor and must not have been modified. """ args, kwargs = totuple for n, value in enumerate(args): if args[n] != self[n]: args[n] = self[n] for key, value in kwargs.items(): start, end = self._names[key] if end: assert is_container(value) for k, j in enumerate(range(start, end)): if kwargs[key][k] != self[j]: kwargs[key][k] = self[j] else: if kwargs[key] != self[start]: kwargs[key] = self[start]
def expand(self, rule, ruleinfo): """Recursively expand wildcards within :class:`RuleInfo` object""" fields = list( filter(lambda x: x is not None, filter(self.expands_field, ruleinfo_fields))) # normalize field values and create namedlist dictionary args = {} for field in fields: attr = getattr(ruleinfo, field) if isinstance(attr, tuple): if len(attr) != 2: raise Exception("Internal Error") # flatten named lists for key in attr[1]: if is_container(attr[1][key]): attr[1][key] = list(flatten(attr[1][key])) # flatten unnamed and overwrite tuples # also turn attr[0] into a list, making it mutable attr = (list(flatten(attr[0])), attr[1]) setattr(ruleinfo, field, attr) args[field] = NamedList(fromtuple=attr) else: args[field] = NamedList() args[field].append(attr) # build graph of expansion dependencies deps = networkx().DiGraph() for field, nlist in args.items(): for n, value in enumerate(nlist): if not isinstance(value, str): # only strings can be expanded continue s = "{}[{}]".format(field, n) # create node for value itself deps.add_node(s, core=True, name=field, idx=n) # node depends on wildcards contained in value deps.add_edges_from((s, t) for t in get_names(value) if t.split(".")[0].split("[")[0] in fields) # field node depends on all it's value nodes deps.add_edge(field, s) # create edges field.name -> field[n] for name, (i, j) in nlist.get_names(): s = "{}.{}".format(field, name) if j is None: j = i + 1 deps.add_edges_from( (s, "{}[{}]".format(field, n)) for n in range(i, j)) # sort variables so that they can be expanded in order try: nodes = list( reversed([ node for node in networkx().algorithms.dag.topological_sort(deps) if deps.out_degree(node) > 0 and 'core' in deps.nodes[node] ])) except networkx().NetworkXUnfeasible: raise CircularReferenceException(deps, rule) from None # expand variables for node in nodes: var_name = deps.nodes[node]['name'] var_idx = deps.nodes[node]['idx'] value = args[var_name][var_idx] if not isinstance(value, str): continue # format what we can valnew = partial_format(value, **args) # check if any remaining wilcards refer to rule fields names = [ re.split(r'\.|\[', name, maxsplit=1)[0] for name in get_names(valnew) ] field_names = ruleinfo_fields[var_name].get('funcparams', []) parm_names = [name for name in field_names if name in names] if parm_names: # Snakemake won't expand wildcards in output of functions, # so we need to format everything here def late_recursion(val, fparms): def wrapper(wildcards, **kwargs): # no partial here, fail if anything left return strip_wildcard_constraints(val).format( **kwargs, **wildcards) # adjust the signature so that snakemake will pass us # everything we need parms = (Parameter(pname, Parameter.POSITIONAL_OR_KEYWORD) for pname in fparms) newsig = signature(wrapper).replace(parameters=parms) wrapper.__signature__ = newsig return wrapper valnew = late_recursion(valnew, parm_names) args[var_name][var_idx] = valnew if ymp.print_rule == 1: log.debug("{}::{}: {} => {}".format(rule.name, node, value, valnew)) # update ruleinfo for name in fields: attr = getattr(ruleinfo, name) if isinstance(attr, tuple): if len(attr) != 2: raise Exception("Internal Error") args[name].update_tuple(attr) else: setattr(ruleinfo, name, args[name][0])
def expand(self, rule, ruleinfo): """Recursively expand wildcards within RuleInfo object""" fields = list( filter(None.__ne__, filter(self.expands_field, ruleinfo_fields))) # normalize field values and create namedlist dictionary args = {} for field in fields: attr = getattr(ruleinfo, field) if isinstance(attr, tuple): if len(attr) != 2: raise Exception("Internal Error") # flatten named lists for key in attr[1]: if is_container(attr[1][key]): attr[1][key] = list(flatten(attr[1][key])) # flatten unnamed and overwrite tuples # also turn attr[0] into a list, making it mutable attr = (list(flatten(attr[0])), attr[1]) setattr(ruleinfo, field, attr) args[field] = NamedList(fromtuple=attr) else: args[field] = NamedList() args[field].append(attr) # build graph of expansion dependencies deps = networkx().DiGraph() for field, nlist in args.items(): for n, value in enumerate(nlist): if not isinstance(value, str): # only strings can be expanded continue s = "{}[{}]".format(field, n) # create node for value itself deps.add_node(s, core=True, name=field, idx=n) # node depends on wildcards contained in value deps.add_edges_from((s, t) for t in get_names(value) if t.split(".")[0].split("[")[0] in fields) # field node depends on all it's value nodes deps.add_edge(field, s) # create edges field.name -> field[n] for name, (i, j) in nlist.get_names(): s = "{}.{}".format(field, name) if j is None: j = i + 1 deps.add_edges_from( (s, "{}[{}]".format(field, n)) for n in range(i, j)) # sort variables so that they can be expanded in order try: nodes = list( reversed([ node for node in networkx().algorithms.dag.topological_sort(deps) if deps.out_degree(node) > 0 and 'core' in deps.nodes[node] ])) except networkx().NetworkXUnfeasible: raise CircularReferenceException(deps, rule) # expand variables for node in nodes: name = deps.nodes[node]['name'] idx = deps.nodes[node]['idx'] value = args[name][idx] if isinstance(value, str): try: value2 = partial_format(value, **args) except FormattingError as e: raise RuleException( "Unable to resolve wildcard '{{{}}}' in parameter {}" "in rule {}".format(e.attr, node, rule.name)) except IndexError as e: raise RuleException( "Unable to format '{}' using '{}'".format(value, args)) args[name][idx] = value2 if ymp.print_rule == 1: log.error("{}::{}: {} => {}".format( rule.name, node, value, value2)) # update ruleinfo for name in fields: attr = getattr(ruleinfo, name) if isinstance(attr, tuple): if len(attr) != 2: raise Exception("Internal Error") args[name].update_tuple(attr) else: setattr(ruleinfo, name, args[name][0])