Example #1
0
    def test_expand_identities(self):
        root = {0:'x'}
        l1 = [{1:'a'}, {1:'b'}, {1:'c'}]
        l2 = [{2:'a'}, {2:'b'}, {2:'c'}]
        l1cp = copy.deepcopy(l1)
        d = {'root': root, 'l1':l1, 'l2':l2}

        try:
            next(namespaces.expand_fuzzyspec_partial(d, ('root', 'l1', 'l2')))
        except StopIteration as e:
            specs = e.value
        else:
            raise RuntimeError()


        self.assertEqual(len(specs), 3*3)

        for i, spec in enumerate(specs):
            res = namespaces.resolve(d, spec)
            namespaces.push_nslevel(res, 'container', {'i':i})


        for  i, spec in enumerate(specs):
            res = namespaces.resolve(d, (*spec, 'container'))
            self.assertEqual(res['i'], i)
            self.assertEqual(res[1], l1cp[i//3][1])
            self.assertEqual(res[2], l2[i%3][2])

            self.assertEqual(res[0], 'x')
Example #2
0
    def test_parse_complex_dicts(self):
        inp = {

         'A': OrderedDict([
               ('sum', None),
               ('three', 33),
               ('y', 10),
              ]),

         'B': OrderedDict([
                ('y', 5),
                ('three', 333),
                ('sum', None),
              ]),
          'four': 4,

        }
        C = BaseConfig(inp)
        ns = ChainMap()
        C.process_fuzzyspec(('A',), ns=ns)
        C.process_fuzzyspec(('B',), ns=ns)

        self.assertNotIn('three', ns)
        self.assertEqual(namespaces.resolve(ns, ('A',))['sum'], 47)
        self.assertEqual(namespaces.resolve(ns, ('B',))['sum'], 342)
Example #3
0
    def test_from_(self):
        inp = {

            'd': {'y': 1},
            'ys': [4, 5, {'from_':'d'}],
            'inner': {'y': {'from_':'d'}, 'three':33},
            'three': 3,
            'four': 4,
            'sum': None,
            'z': {'from_':'d'},
        }
        c = BaseConfig(inp)
        ns = ChainMap()
        specs = c.process_fuzzyspec(('ys',), ns=ns)
        ns_ = namespaces.resolve(ns, specs[2])
        self.assertEqual(ns_['y'], 1)
        self.assertEqual(c.resolve_key('sum', ns=ns_)[1], 8)

        #ns = ChainMap()

        spec, = c.process_fuzzyspec(('inner',), ns=ns)

        ns_ = namespaces.resolve(ns, spec)

        self.assertEqual(ns_['y'], 1)
        self.assertEqual(c.resolve_key('sum', ns=ns_)[1], 38)

        with self.assertRaises(ConfigError):
            c.resolve_key('z', ns)
Example #4
0
    def test_resolve(self):

        rem, ns = namespaces.resolve_partial(d, ('a',))
        self.assertFalse(rem)
        self.assertEqual(ns, ChainMap(a,d))

        rem, ns = namespaces.resolve_partial(d, (('c',1),))
        self.assertFalse(rem)
        self.assertEqual(ns, ChainMap(c[1],d))

        spec =  ('a','b', ('c',1))
        rem, ns = namespaces.resolve_partial(d, spec)
        self.assertFalse(rem)
        self.assertEqual(ns, namespaces.resolve(d,spec))

        rem, ns = namespaces.resolve_partial(d, ('x','a', 'b'))
        self.assertEqual(list(rem), ['x', 'a', 'b'])
        self.assertEqual(len(ns.maps), 1)

        spec = ('a', 'b', ('x', 0))
        rem, ns = namespaces.resolve_partial(d, spec)
        self.assertEqual(list(rem), [('x', 0)])
        self.assertEqual(len(ns.maps), 3)
        with self.assertRaises(KeyError):
            namespaces.resolve(d, spec)
Example #5
0
    def test_identities(self):
        a = {1:'a'}
        alta = {1:'aa'}
        b = {2:'b'}
        d = {'a':a, 'alta':alta, 'b':b}
        m1 = namespaces.resolve(d, ('a','b'))
        m2 = namespaces.resolve(d, ('a','b'))
        m3 = namespaces.resolve(d, ('alta','b'))

        self.assertIs(m1.maps[0], m2.maps[0])
        self.assertFalse(m3.maps[0] is m2.maps[0])
Example #6
0
    def test_fuzzy(self):
        inp = {'four': 4, 'ys': [-1,-2,-3,-4], 'sum':None}
        c = BaseConfig(inp)
        ns = ChainMap()
        ret = c.process_fuzzyspec(('ys',), ns=ns)
        for spec, s in zip(ret, (6,5,4,3)):
            ns_ = namespaces.resolve(ns, spec)
            c.resolve_key('sum', ns=ns_)
            self.assertEqual(ns_['sum'], s)

        #specs have to be persistent
        self.assertEqual(namespaces.resolve(ns, spec).maps[0],
                         {'sum': 3, 'y': -4})
Example #7
0
    async def _run_parallel(self, deps, completed_spec):
        try:
            runnable_specs = deps.send(completed_spec)
        except StopIteration:
            return
        pending_tasks = {}
        for pending_spec in runnable_specs:
            if isinstance(pending_spec, (CollectSpec, CollectMapSpec)):
                remote_coro = _async_identity(
                    pending_spec.function, namespaces.resolve(self.rootns, pending_spec.nsspec)
                )
            else:
                remote_coro = curio.run_in_process(
                    self.get_result, pending_spec.function, *self.resolve_callargs(pending_spec)
                )
            pending_task = await curio.spawn(remote_coro)
            pending_tasks[pending_task] = pending_spec

        next_runs = []
        waiter = curio.wait(pending_tasks)
        async for completed_task in waiter:
            try:
                result = await completed_task.join()
            except curio.TaskError as e:
                raise curio.KernelExit() from e

            new_completed_spec = pending_tasks.pop(completed_task)
            self.set_result(result, new_completed_spec)
            next_runs_coro = self._run_parallel(deps, new_completed_spec)
            next_runs.append(await curio.spawn(next_runs_coro))

        for wait_run in next_runs:
            await wait_run.join()
Example #8
0
def get_nice_name(ns, nsspec, suffix=None):
    """Get a name by quering the parts of a namespace specification.
    ``ns`` should be a namespace ChainMap and ``nsspec`` a
    tuple with a valid specification
    (see the ``namespaces`` documentation for more details)"""
    parts = []
    currspec = []
    currns = ns
    for ele in nsspec:
        currspec.append(ele)
        val = namespaces.value_from_spcec_ele(currns, ele)

        # kind of ugly, but we don't want to dumpt compound types, and too long
        # filenames)
        if isinstance(val, (list, dict, set, tuple, frozenset)):
            val = str(ele)
        else:
            try:
                val = str(val)
            except Exception as e:
                log.debug("Could not convert a value (%r) to string: %s" % (val, e))
                val = str(ele)
            else:
                if len(val) > 25:
                    val = str(ele)

        parts.append(normalize_name(val))

        currns = namespaces.resolve(ns, currspec)

    if suffix:
        parts.append(suffix)

    return "_".join(parts)
Example #9
0
 def execute_sequential(self):
     for node in self.graph:
         callspec = node.value
         if isinstance(callspec, (CollectSpec, CollectMapSpec)):
             result = callspec.function(namespaces.resolve(self.rootns, callspec.nsspec))
         else:
             result = self.get_result(callspec.function, *self.resolve_callargs(callspec))
         self.set_result(result, callspec)
Example #10
0
 def resolve_kwargs(self, nsspec, kwargs):
     namespace = namespaces.resolve(self.rootns, nsspec)
     kwdict = {}
     put_index = len(namespace.maps) - 1
     for kw in kwargs:
         index, kwdict[kw] =  namespace.get_where(kw)
         if index < put_index:
             put_index = index
     kwdict = {kw: namespace[kw] for kw in kwargs}
     return kwdict, put_index
Example #11
0
    def resolve_callargs(self, callspec):
        function, kwargs, resultname, nsspec = callspec
        namespace = namespaces.resolve(self.rootns, nsspec)
        kwdict = {kw: namespace[kw] for kw in kwargs}
        if hasattr(function, "prepare"):
            prepare_args = function.prepare(spec=callspec, namespace=namespace, environment=self.environment)
        else:
            prepare_args = {}

        return kwdict, prepare_args
Example #12
0
    def test_remove_outer(self):
        targets = [Target('fruit', (['inner']), ())]
        c = Config({'apple': True, 'inner':{'orange':False}})

        provider = Provider()
        builder = ResourceBuilder(targets=targets, providers=provider,
                                  input_parser=c)
        builder.resolve_targets()
        builder.execute_sequential()
        ns = namespaces.resolve(builder.rootns, ('inner',))
        self.assertEqual(ns['fruit'], (None, False))
Example #13
0
    def set_result(self, result, spec):
        function, _, resultname, nsspec = spec
        namespace = namespaces.resolve(self.rootns, nsspec)
        put_map = namespace.maps[1]
        log.debug("Setting result for %s %s", spec, nsspec)

        if resultname in put_map:
            raise ValueError("Resource already set: %s" % resultname)
        put_map[resultname] = result

        for action, args in self._node_flags[spec]:
            action(result, self.rootns, spec, **dict(args))
Example #14
0
    def _process_requirement(self, name, nsspec, *, extraargs=None, default=EMPTY, parents=None):
        """Create nodes so as to satisfy the requirement specified by the
        arguments."""
        if parents is None:
            parents = []

        log.debug("Processing requirement: %s" % (name,))

        ns = namespaces.resolve(self.rootns, nsspec)
        if extraargs is None:
            extraargs = ()

        # First try to find the name in the namespace
        try:
            put_index, val = self.input_parser.resolve_key(name, ns, parents=parents)
            log.debug("Found %s for spec %s at %s" % (name, nsspec, put_index))

        except InputNotFoundError as e:
            # See https://www.python.org/dev/peps/pep-3110/
            saved_exception = e
            # Handle this case later
            pass
        else:
            if extraargs:
                raise ResourceNotUnderstood(
                    name,
                    "The resource %s name is "
                    "already present in the input, but some arguments were "
                    "passed to compute it: %s" % (name, extraargs),
                    parents[-1],
                )

            if isinstance(val, ExplicitNode):
                yield from self._make_node((name, val.value), nsspec, extraargs, parents)
            else:
                yield put_index, val
            return

        # If the name is not in the providers, either it is an extra argument
        # or is missing

        if not self.is_provider_func(name):
            if default is EMPTY:
                raise saved_exception
            else:
                put_index = None
                yield put_index, default
                return

        # here we handle the case where the requirement is a provider and
        # make a new node for it.
        yield from self._make_node(name, nsspec, extraargs, parents)
Example #15
0
    def _create_default_key(self, name, nsspec, put_index=None, defaults=None):
        """Push a namespace level for a node to store input values.
        Return the nsspec of that node."""
        if defaults is None:
            defaults = {}
        if put_index is None:
            put_index = 0

        defaults_label = "_" + name + "_defaults"
        nsspec = (*nsspec[: len(nsspec) - put_index], defaults_label)
        parent_ns = namespaces.resolve(self.rootns, nsspec[:-1])
        namespaces.push_nslevel(parent_ns, defaults_label, defaults)
        return nsspec
Example #16
0
 def test_nsexpand(self):
     spec = ('pdfsets', 'theories', 'datasets')
     c = Config(inp)
     ns = utils.ChainMap()
     specs = c.process_fuzzyspec(spec, ns=ns)
     self.assertEqual(len(specs), 8)
     datasets = [
           'ds: d1 (theory: th 1)',
           'ds: d2 (theory: th 1)',
           'ds: d1 (theory: th 2)',
           'ds: d2 (theory: th 2)',
           'ds: d1 (theory: th 1)',
           'ds: d2 (theory: th 1)',
           'ds: d1 (theory: th 2)',
           'ds: d2 (theory: th 2)']
     for spec, ds in zip(specs, datasets):
         self.assertEqual(namespaces.resolve(ns, spec)['dataset'], ds)
Example #17
0
    def _make_collect_targets(self, colltargets, name, nsspec, parents):

        newparents = [name, *parents]

        myspec = self._create_default_key(name, nsspec)

        my_node = CollectMapSpec(colltargets, (), name, myspec)

        required_by = yield 0, my_node

        if required_by is None:
            outputs = set()
        else:
            outputs = set([required_by])

        self.graph.add_or_update_node(my_node, outputs=outputs)

        myns = namespaces.resolve(self.rootns, myspec)
        myns[collect.resultkey] = {}
        tlens = myns[target_map.targetlenskey] = {}

        for target in colltargets.targets:
            target_specs = self.expand_target_spec(target)
            tlens[target] = len(target_specs)
            for i, tspec in enumerate(target_specs):
                gen = self._process_requirement(
                    name=target.name, nsspec=tspec, extraargs=target.extraargs, parents=newparents
                )
                index, tnode = gen.send(None)
                try:
                    gen.send(my_node)
                except StopIteration:
                    pass
                else:
                    raise RuntimeError()

                # This allows to map directly inputs regardless of whether they
                # are nodes or not.
                if isinstance(tnode, Node):
                    flagargs = (("target", my_node), ("index", (target, i)))
                    self._node_flags[tnode].add((add_to_dict_flag, flagargs))
                else:
                    myns[collect.resultkey][(target, i)] = tnode
Example #18
0
 def set_result(self, result, spec, put_index):
     function, kwargs, resultname, execmode, nsspec = spec
     log.debug("Setting %s in %s" % (str(spec), str(nsspec)))
     namespace = namespaces.resolve(self.rootns, nsspec)
     put_map = namespace.maps[put_index]
     if not execmode in ExecModes:
         raise TypeError("Callspecmode must be an ExecMode")
     if execmode == ExecModes.SET_UNIQUE:
         if resultname in put_map:
             raise ValueError("Resource already set: %s" % resultname)
         put_map[resultname] = result
     elif execmode == ExecModes.SET_OR_UPDATE:
         put_map[resultname] = result
     elif execmode == ExecModes.APPEND_UNORDERED:
         if not resultname in namespace:
             put_map[resultname] = []
         put_map[resultname].append(result)
     else:
         raise NotImplementedError(execmode)
Example #19
0
    def process_requirement(self, name, nsspec, extraargs=None, required_by=None,
                            default=EMPTY):

        ns = namespaces.resolve(self.rootns, nsspec)
        if extraargs is None:
            extraargs = ()
        try:
            self.input_parser.resolve_key(name, ns, parents=[required_by])
        except KeyError as e:
            if hasattr(self.providers, name):
                f = getattr(self.providers, name)
                s = inspect.signature(f)
                if(extraargs):
                    ns.update(dict(extraargs))
                cs = CallSpec(f, tuple(s.parameters.keys()), name, ExecModes.SET_UNIQUE,
                              nsspec)
                self.graph.add_or_update_node(cs)
                for param_name, param in s.parameters.items():
                    self.process_requirement(param_name, nsspec, None,
                                             required_by=cs,
                                             default=param.default)
                if required_by is None:
                    outputs = set()
                else:
                    outputs = set([required_by])
                self.graph.add_or_update_node(cs, outputs=outputs)

                if hasattr(f, 'checks'):
                    for check in f.checks:
                        check(cs, ns, self.graph)
            else:
                if default is EMPTY:
                    raise e
                else:
                    ns[name] = default


        else:
            if extraargs:
                raise ResourceNotUnderstood("The resource %s name is "
                "already present in the input, but some arguments were "
                "passed to compute it: %s" % (name, extraargs))
Example #20
0
    def _make_collect(self, f, name, nsspec, parents):
        """Make a node that spans a function over the values in a list and
        collects them in another list."""
        newparents = [name, *parents]

        myspec = self._create_default_key(name, nsspec)

        collspec = CollectSpec(f, (), name, myspec)
        log.debug("Appending node {}".format(collspec))

        required_by = yield 0, collspec

        if required_by is None:
            outputs = set()
        else:
            outputs = set([required_by])

        self.graph.add_or_update_node(collspec, outputs=outputs)

        total_fuzzyspec = nsspec + f.fuzzyspec
        specs = self.input_parser.process_fuzzyspec(total_fuzzyspec, self.rootns, newparents)
        myns = namespaces.resolve(self.rootns, myspec)
        myns[collect.resultkey] = OrderedDict.fromkeys(range(len(specs)))
        for i, spec in enumerate(specs):
            gen = self._make_node(f.function.__name__, spec, None, parents=newparents)

            index, newcs = gen.send(None)
            try:
                gen.send(collspec)
            except StopIteration:
                pass
            else:
                raise RuntimeError()

            flagargs = (("target", collspec), ("index", i))
            self._node_flags[newcs].add((add_to_dict_flag, flagargs))
Example #21
0
    def _make_callspec(self, f, name, nsspec, extraargs, parents):
        """Make a normal node that calls a function."""

        defaults = {}
        s = inspect.signature(f)
        if extraargs:
            defaults.update(dict(extraargs))

        # Note that this is the latest possible put_index and not len - 1
        # because there is also the root namespace.
        put_index = len(nsspec)
        gens = []
        for param_name, param in s.parameters.items():
            default = defaults.get(param_name, param.default)
            gen = self._process_requirement(
                param_name, nsspec, extraargs=None, default=default, parents=[name, *parents]
            )
            index, _ = gen.send(None)
            log.debug("put_index for %s is %s" % (param_name, index))
            if index is None:
                defaults[param_name] = default
            elif index < put_index:
                put_index = index
            gens.append(gen)

        # The namespace stack (put_index) goes in the opposite direction
        # of the nsspec. put_index==len(nsspec)==len(ns.maps)-1
        # corresponds to the root namespace, and put_index=0 to the current
        # spec.

        # We need the len bit for the case put_index==0
        newnsspec = self._create_default_key(name, nsspec, put_index, defaults)
        log.debug("New spec for %s is: %s" % (name, newnsspec))

        ns = namespaces.resolve(self.rootns, newnsspec)

        cs = CallSpec(f, tuple(s.parameters.keys()), name, newnsspec)
        already_exists = cs in self.graph
        if already_exists:
            log.debug("Node '%s' already in the graph.", cs)
        else:
            log.debug("Appending node '%s'." % (cs,))
            self.graph.add_or_update_node(cs)
        for gen in gens:
            try:
                gen.send(cs)
            except StopIteration:
                pass
            else:
                raise RuntimeError()

        required_by = yield put_index, cs
        if required_by is None:
            outputs = set()
        else:
            outputs = set([required_by])
        self.graph.add_or_update_node(cs, outputs=outputs)

        # Do not repeat the checks for the same node
        if already_exists:
            return

        try:
            check_types(f, ns)
        except BadInputType as e:
            raise ResourceError(name, e, parents) from e

        if hasattr(f, "checks"):
            for check in f.checks:
                try:
                    check(callspec=cs, ns=ns, graph=self.graph, environment=self.environment)
                except CheckError as e:
                    raise ResourceError(name, e, parents) from e
Example #22
0
 def nsspec(x, beginning=()):
     ns = namespaces.resolve(self.rootns, beginning)
     default_label =  '_default' + str(x)
     namespaces.push_nslevel(ns, default_label)
     return beginning + (default_label,)
Example #23
0
def add_to_dict_flag(result, ns, origin, target, index):
    log.debug("Setting element %s of %r from %r", index, target, origin)
    namespaces.resolve(ns, target.nsspec)[collect.resultkey][index] = result