diff --git a/core/sheerka.py b/core/sheerka.py index 17f2e64..8d60fd2 100644 --- a/core/sheerka.py +++ b/core/sheerka.py @@ -65,17 +65,8 @@ class Sheerka(Concept): self.sdp.set_key(self.USER_CONCEPTS_KEYS, 1000) self.initialize_builtin_concepts() - - self.parsers.append(core.utils.get_class("parsers.DefaultParser.DefaultParser")) - self.parsers.append(core.utils.get_class("parsers.PythonParser.PythonParser")) - self.parsers.append(core.utils.get_class("parsers.ExactConceptParser.ExactConceptParser")) - - self.evaluators.append(core.utils.new_object("evaluators.ParsersEvaluator.ParsersEvaluator")) - self.evaluators.append(core.utils.new_object("evaluators.AddConceptEvaluator.AddConceptEvaluator")) - self.evaluators.append(core.utils.new_object("evaluators.PythonEvaluator.PythonEvaluator")) - self.evaluators.append(core.utils.new_object("evaluators.ConceptEvaluator.ConceptEvaluator")) - self.evaluators.append( - core.utils.new_object("evaluators.DuplicateConceptEvaluator.DuplicateConceptEvaluator")) + self.initialize_builtin_parsers() + self.initialize_builtin_evaluators() except IOError as e: return ReturnValueConcept(self, False, self.get(BuiltinConcepts.ERROR), e) @@ -123,6 +114,28 @@ class Sheerka(Concept): self.add_in_cache(concept) + def initialize_builtin_parsers(self): + """ + Init the parsers + :return: + """ + for parser in core.utils.get_sub_classes("parsers", "parsers.BaseParser.BaseParser"): + log.debug(f"Adding builtin parser '{parser.__name__}'") + self.parsers.append(parser) + + def initialize_builtin_evaluators(self): + """ + Init the evaluators + :return: + """ + for evaluator in core.utils.get_sub_classes("evaluators", "evaluators.BaseEvaluator.OneReturnValueEvaluator"): + log.debug(f"Adding builtin evaluator '{evaluator.__name__}'") + self.evaluators.append(evaluator) + + for evaluator in core.utils.get_sub_classes("evaluators", "evaluators.BaseEvaluator.AllReturnValuesEvaluator"): + log.debug(f"Adding builtin evaluator '{evaluator.__name__}'") + self.evaluators.append(evaluator) + def init_logging(self): if self.debug: log_format = "%(asctime)s %(name)s [%(levelname)s] %(message)s" @@ -197,17 +210,18 @@ class Sheerka(Concept): def process(self, context, return_values, contextual_concepts=None): log.debug("Evaluating parsing result.") - # init + # return_values must be a list if not isinstance(return_values, list): return_values = [return_values] + # adds contextual concepts if contextual_concepts: return_values.extend(contextual_concepts) # group the evaluators by priority and sort them # The first one to be applied will be the one with the highest priority grouped_evaluators = {} - for item in self.evaluators: + for item in [e() for e in self.evaluators]: grouped_evaluators.setdefault(item.priority, []).append(item) sorted_priorities = sorted(grouped_evaluators.keys(), reverse=True) @@ -428,6 +442,19 @@ class Sheerka(Concept): return res + @staticmethod + def get_builtin_parsers(): + res = [] + # modules = core.utils.get_module("parsers") + # for m in modules: + base_class = core.utils.get_class("parsers.BaseParser.BaseParser") + + for c in core.utils.get_classes_recursive("parsers"): + #if issubclass(c, base_class) and c != base_class: + res.append(c) + + return res + @staticmethod def test(): return "I have access to Sheerka !" diff --git a/core/utils.py b/core/utils.py index 26f9c25..2e629a1 100644 --- a/core/utils.py +++ b/core/utils.py @@ -1,4 +1,6 @@ +import importlib import inspect +import pkgutil import sys @@ -38,6 +40,7 @@ def get_class(qname): m = getattr(m, comp) return m + def get_module(qname): """ Loads a module from its full qualified name @@ -77,6 +80,11 @@ def get_full_qualified_name(obj): def get_classes(module_name): + """ + Gets all classes, for a given module_name + :param module_name: name of the module + :return: + """ mod = get_module(module_name) for name in dir(mod): obj = getattr(mod, name) @@ -84,6 +92,30 @@ def get_classes(module_name): yield obj +def get_classes_from_package(package_name): + """ + Gets all classes, for a given package + :param package_name: name of the package + :return: + """ + pkg = __import__(package_name) + prefix = pkg.__name__ + "." + for importer, modname, ispkg in pkgutil.iter_modules(pkg.__path__, prefix): + for c in get_classes(modname): + yield c + + +def get_sub_classes(package_name, base_class_name): + + pkg = __import__(package_name) + prefix = pkg.__name__ + "." + for (module_loader, name, ispkg) in pkgutil.iter_modules(pkg.__path__, prefix): + importlib.import_module(name) + + base_class = get_class(base_class_name) + return base_class.__subclasses__() + + def remove_from_list(lst, to_remove): """ Removes elements from a list if they exist diff --git a/tests/test_utils.py b/tests/test_utils.py index fc67b1e..8288ed5 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -6,7 +6,7 @@ import pytest (None, "",), ([], ""), (["hello", "world"], "hello world"), - # (["hello world", "my friend"], '"hello world" "my friend"') + # (["hello world", "my friend"], '"hello world" "my friend"') ]) def test_i_can_create_string_from_a_list(lst, as_string): assert core.utils.sysarg_to_string(lst) == as_string @@ -20,3 +20,39 @@ def test_i_can_get_classes(): assert len(classes) > 2 assert error_concept in classes assert return_value_concept in classes + + +def test_i_can_get_base_classes(): + classes = list(core.utils.get_classes_from_package("parsers")) + + # example of classes that should be in the result + base_parser = core.utils.get_class("parsers.BaseParser.BaseParser") + default_parser = core.utils.get_class("parsers.DefaultParser.DefaultParser") + exact_concept_parser = core.utils.get_class("parsers.ExactConceptParser.ExactConceptParser") + python_parser = core.utils.get_class("parsers.PythonParser.PythonParser") + node = core.utils.get_class("parsers.BaseParser.Node") + def_concept_node = core.utils.get_class("parsers.DefaultParser.DefConceptNode") + python_node = core.utils.get_class("parsers.PythonParser.PythonNode") + + assert base_parser in classes + assert default_parser in classes + assert exact_concept_parser in classes + assert python_parser in classes + assert node in classes + assert def_concept_node in classes + assert python_node in classes + + +def test_i_can_get_sub_classes(): + sub_classes = core.utils.get_sub_classes("parsers", "parsers.BaseParser.BaseParser") + + # example of classes that should be (or not) in the result + base_parser = core.utils.get_class("parsers.BaseParser.BaseParser") + default_parser = core.utils.get_class("parsers.DefaultParser.DefaultParser") + exact_concept_parser = core.utils.get_class("parsers.ExactConceptParser.ExactConceptParser") + python_parser = core.utils.get_class("parsers.PythonParser.PythonParser") + + assert base_parser not in sub_classes + assert default_parser in sub_classes + assert exact_concept_parser in sub_classes + assert python_parser in sub_classes