diff --git a/src/reportengine/namespaces.py b/src/reportengine/namespaces.py index d78499d..2cfc332 100644 --- a/src/reportengine/namespaces.py +++ b/src/reportengine/namespaces.py @@ -99,9 +99,9 @@ def expand_fuzzyspec_partial(ns, fuzzyspec, currspec=None): results = [] #ns = ChainMap(d) key, remainder = fuzzyspec[0], fuzzyspec[1:] - if not key in ns: - yield key, currspec, ns + yield key, currspec, ns val = ns[key] + if isinstance(val, Mapping): cs_ = (*currspec, key) @@ -129,12 +129,17 @@ def expand_fuzzyspec(ns, fuzzyspec, currspec=None): """Return all the nsspecs that spawn from the fuzzyspec. Raise ElementNotFound if some part is missing.""" gen = expand_fuzzyspec_partial(ns, fuzzyspec, currspec) - try: - missing, nsspec, _ = gen.send(None) - except StopIteration as e: - return e.value - raise ElementNotFound("Could not resolve a fuzzyspec. " - "A key is missing: %s, at the level %r." % (missing, nsspec)) + while True: + try: + key, nsspec, currns = gen.send(None) + except StopIteration as e: + return e.value + else: + if key not in currns: + raise ElementNotFound( + "Could not resolve a fuzzyspec. " + f"A key is missing: '{key}', at the level {nsspec}." + ) def collect_fuzzyspec(ns, key, fuzzyspec, currspec=None): diff --git a/src/reportengine/resourcebuilder.py b/src/reportengine/resourcebuilder.py index f31f971..a14fc80 100644 --- a/src/reportengine/resourcebuilder.py +++ b/src/reportengine/resourcebuilder.py @@ -402,11 +402,16 @@ def _process_requirement(self, name, nsspec, *, extraargs=None, log.debug("Processing requirement: %s" % (name,)) + ns = namespaces.resolve(self.rootns, nsspec) if extraargs is None: extraargs = () - + is_provider = self.is_provider_func(name) + if ( is_provider and isinstance(self.get_provider_func(name), collect)): + log.debug("Resolving collect node for %s", name) + yield from self._make_node(name, nsspec, extraargs, parents) + return #First try to find the name in the namespace try: put_index, val = self.input_parser.resolve_key(name, ns, parents=parents, currspec=nsspec) diff --git a/src/reportengine/tests/test_complexinput.py b/src/reportengine/tests/test_complexinput.py index d7542c3..a3ff729 100644 --- a/src/reportengine/tests/test_complexinput.py +++ b/src/reportengine/tests/test_complexinput.py @@ -181,6 +181,12 @@ def produce_matched_datasets_from_dataspecs(self, dataspecs): res.sort(key=lambda x: (x['dataset_name'])) return res + def produce_dependent_namespace(self, pdf): + return {"pdfprop": pdf} + + def produce_derived_prop(self, pdfprop): + return f"Derived: {pdfprop}" + @make_argcheck def bad_check(pdf): @@ -193,8 +199,15 @@ def report(self, template_text): def plot_a_pdf(self, pdf): return "PLOT OF " + str(pdf) + def prop_table(self, pdfprop): + return f"Table: {pdfprop}" + dataspecs_speclabel = collect('speclabel', ('datasepcs',), element_default='label') + + props_collection = collect("prop_table", ("dependent_namespace",)) + resolved_collection = collect("derived_prop", ("dependent_namespace",)) + @bad_check def bad_plot(self, pdf): return self.plot_a_pdf(pdf) @@ -399,5 +412,35 @@ def test_namespace_production(self): (('matched_datasets_from_dataspecs', 0), ('dataspecs', 1),))['dataset'] assert ds1 != ds2 + def test_dependent_rules(self): + c = Config({"pdf": "a", "Ns": {"pdf": "b"}}) + targets = [ + FuzzyTarget("props_collection", (), (), ()), + FuzzyTarget("props_collection", ("Ns",), (), ()), + ] + builder = resourcebuilder.ResourceBuilder(c, Providers(), targets) + builder.resolve_fuzzytargets() + builder.execute_sequential() + res1 = namespaces.resolve(builder.rootns, ())["props_collection"] + res2 = namespaces.resolve(builder.rootns, ('Ns',))["props_collection"] + assert res1 == ['Table: PDF: a'] + assert res2 == ['Table: PDF: b'] + + def test_dependent_resolved(self): + c = Config({"pdf": "a", "Ns": {"pdf": "b"}}) + targets = [ + FuzzyTarget("resolved_collection", (), (), ()), + FuzzyTarget("resolved_collection", ("Ns",), (), ()), + ] + builder = resourcebuilder.ResourceBuilder(c, Providers(), targets) + builder.resolve_fuzzytargets() + builder.execute_sequential() + res1 = namespaces.resolve(builder.rootns, ())["resolved_collection"] + res2 = namespaces.resolve(builder.rootns, ('Ns',))["resolved_collection"] + assert res1 == ['Derived: PDF: a'] + assert res2 == ['Derived: PDF: b'] + + + if __name__ == '__main__': unittest.main() diff --git a/src/reportengine/tests/test_namespaces.py b/src/reportengine/tests/test_namespaces.py index 803d086..fcad53b 100644 --- a/src/reportengine/tests/test_namespaces.py +++ b/src/reportengine/tests/test_namespaces.py @@ -52,52 +52,26 @@ def test_resolve(self): def test_expand(self): fuzzy = ('a', 'c') ns = ChainMap(self.d) - gen = namespaces.expand_fuzzyspec_partial(ns, fuzzy) - #self.assertFalse(list(gen)) - while True: - try: - next(gen) - except StopIteration as e: - self.assertEqual(e.value, [('a', ('c', 0)), ('a', ('c', 1))]) - break - else: - self.fail() - - fuzzy = ('a', 'x', 'c') - ns = ChainMap(self.d) - gen = namespaces.expand_fuzzyspec_partial(ns, fuzzy) - #self.assertFalse(list(gen)) - var, spec, cns = next(gen) - cns[var] = 'not ok' - with self.assertRaises(TypeError): - next(gen) - - fuzzy = ('a', 'xx', 'c') - ns = ChainMap(self.d) - gen = namespaces.expand_fuzzyspec_partial(ns, fuzzy) - #self.assertFalse(list(gen)) - var, spec, cns = next(gen) - cns[var] = [{'ok': True}, {'ok':'yes'}, {'ok':1}] - with self.assertRaises(StopIteration) as ec: - next(gen) - specs = ec.exception.value - self.assertEqual(set(specs), - set(itertools.product('a', [('xx', 0), ('xx', 1,), ('xx', 2)], - [('c', 0), ('c', 1)])) - ) + assert namespaces.expand_fuzzyspec(ns, fuzzy) == [ + ('a', ('c', 0)), + ('a', ('c', 1)), + ] + def test_nested_expand(self): d = self.d - d['c'][0]['l3'] = [{'x':1},{'x':2}] - d['c'][1]['l3'] = [{'x':1},] + d['c'][0]['l3'] = [{'x': 1}, {'x': 2}] + d['c'][1]['l3'] = [ + {'x': 1}, + ] ns = ChainMap(d) fuzzy = ('c', 'l3') - gen = namespaces.expand_fuzzyspec_partial(ns, fuzzy) - with self.assertRaises(StopIteration) as ec: - next(gen) - self.assertEqual(ec.exception.value, - [(('c', 0), ('l3', 0)), (('c', 0), ('l3', 1)), (('c', 1), ('l3', 0))] - ) + res = namespaces.expand_fuzzyspec(ns, fuzzy) + assert res == [ + (('c', 0), ('l3', 0)), + (('c', 0), ('l3', 1)), + (('c', 1), ('l3', 0)), + ] def test_identities(self): a = {1:'a'} @@ -118,12 +92,7 @@ def test_expand_identities(self): 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() + specs = namespaces.expand_fuzzyspec(d, ('root', 'l1', 'l2')) self.assertEqual(len(specs), 3*3)