configurationengine/source/plugins/common/ConeRulePlugin/ruleplugin/ruleml.py
changeset 3 e7e0ae78773e
parent 0 2e8eeb919028
equal deleted inserted replaced
2:87cfa131b535 3:e7e0ae78773e
    17 A plugin implementation for rule generation.
    17 A plugin implementation for rule generation.
    18 '''
    18 '''
    19 
    19 
    20 
    20 
    21 import os
    21 import os
    22 import sys
       
    23 import logging
    22 import logging
    24 import shutil
    23 import pkg_resources
    25 
       
    26 import __init__
       
    27 import re
       
    28 
    24 
    29 from ruleplugin import relations
    25 from ruleplugin import relations
    30 from cone.public import exceptions,plugin,utils,api,rules
    26 from cone.public import plugin,utils,rules
    31 
    27 
    32 class RuleImpl(plugin.ImplBase):
    28 class RuleImpl(plugin.ImplBase):
    33     """
       
    34     MakeImpl plugin finds feature references that are configured in a .ruleml file
       
    35     and generate a rule from them
       
    36     """
       
    37     IMPL_TYPE_ID = 'ruleml'
    29     IMPL_TYPE_ID = 'ruleml'
    38     DEFAULT_INVOCATION_PHASE = 'pre'
    30     DEFAULT_INVOCATION_PHASE = 'pre'
    39     
    31     
    40     def __init__(self, ref, configuration, relation_container):
    32     def __init__(self, ref, configuration, relation_container):
    41         """
    33         """
    42         Overloading the default constructor
    34         Overloading the default constructor
    43         """
    35         """
    44         plugin.ImplBase.__init__(self,ref,configuration)
    36         plugin.ImplBase.__init__(self,ref,configuration)
    45         self.logger = logging.getLogger('cone.ruleml(%s)' % self.ref)
       
    46         self.relation_container = relation_container
    37         self.relation_container = relation_container
    47 
    38 
    48     def list_output_files(self):
    39     def list_output_files(self):
    49         """
    40         """
    50         Return a list of output files as an array. 
    41         Return a list of output files as an array. 
    51         """
    42         """
    52         return []
    43         return []
    53     
    44     
       
    45     def get_refs(self):
       
    46         """
       
    47         Return a list of all ConfML setting references that affect this
       
    48         implementation. May also return None if references are not relevant
       
    49         for the implementation.
       
    50         """
       
    51         refs = []
       
    52         relations = self.get_relations()
       
    53         for relation in relations:
       
    54             # get refs from relation return a tuple (left side refs, right side refs)
       
    55             # only the left side refs are the "input" refs  
       
    56             refs += relation.get_refs()
       
    57         # If the rules do not have any references return None to disable filter by refs 
       
    58         if refs == []: 
       
    59             refs = None
       
    60         return refs
       
    61     
       
    62     def get_target_refs(self):
       
    63         """
       
    64         Return a list of all ConfML setting references that are affected by this
       
    65         implementation. May also return None if references are not relevant
       
    66         for the implementation.
       
    67         """
       
    68         refs = []
       
    69         relations = self.get_relations()
       
    70         for relation in relations:
       
    71             refs += relation.get_set_refs()
       
    72         return refs
       
    73 
       
    74     def get_outputs(self):
       
    75         """
       
    76         Return a list of GenerationOutput objets as a list. 
       
    77         """
       
    78         outputs = []
       
    79         phase = None 
       
    80         if self.generation_context: phase = self.generation_context.phase
       
    81         for rel in self.get_relations():
       
    82             outrefs = rel.get_set_refs()
       
    83             for ref in outrefs:
       
    84                 outputs.append(plugin.GenerationOutput(ref,rel,type='ref', phase=phase))
       
    85         return outputs
       
    86     
    54     def generate(self, context=None):
    87     def generate(self, context=None):
    55         self.logger.info("Generating rules from %s" % self.ref)
    88         logging.getLogger('cone.ruleml(%s)' % self.ref).info("Generating rules from %s" % self.ref)
    56         relation_container = self.get_relation_container()
    89         relation_container = self.get_relation_container()
    57         relation_container.context = context
    90         relation_container.context = context
    58         return relation_container.execute()
    91         return relation_container.execute(context)
    59     
       
    60     def has_tag(self, tags, policy=None):
       
    61         # RuleML should always be executed, regardless of the tags
       
    62         return True
       
    63     
    92     
    64     def get_relation_container(self):
    93     def get_relation_container(self):
    65         return self.relation_container
    94         return self.relation_container
       
    95 
       
    96     def get_relations(self):
       
    97         return self.relation_container.get_relations()
       
    98 
       
    99 class RuleBuiltinsModule(object):
       
   100     pass
    66 
   101 
    67 class RulemlRelationContainer(plugin.RelationContainer):
   102 class RulemlRelationContainer(plugin.RelationContainer):
    68     """
   103     """
    69     Relation container for RuleML rules.
   104     Relation container for RuleML rules.
    70     
   105     
    71     Basically this is a wrapper for rules.RelationContainer that adapts
   106     Basically this is a wrapper for rules.RelationContainer that adapts
    72     it to the interface of plugin.RelationContainer.
   107     it to the interface of plugin.RelationContainer.
    73     """
   108     """
    74     def __init__(self, configuration, source, rule_list, eval_globals):
   109     def __init__(self, configuration, source, rule_list, eval_globals):
    75         plugin.RelationContainer.__init__(self, configuration, source=source)
   110         plugin.RelationContainer.__init__(self, configuration, source=source)
    76         self.logger = logging.getLogger('cone.ruleml_relation_container(%s)' % self.source)
       
    77         self.configuration = configuration
   111         self.configuration = configuration
    78         self.relation_container = rules.RelationContainerImpl()
   112         self.relation_container = rules.RelationContainerImpl()
    79         self.eval_globals = eval_globals
   113         self.eval_globals = eval_globals
    80         self.context = None
   114         self.context = None
    81         for rule in rule_list:
   115         for rule in rule_list:
    82             self.relation_container.add_relation(rule)
   116             self.relation_container.add_relation(rule)
    83     
   117     
    84     def execute(self):
   118     def execute(self, context=None):
    85         results = []
   119         results = []
    86         
   120         if context: self.context = context
       
   121          
    87         # Create the autoconfig if not done already
   122         # Create the autoconfig if not done already
    88         plugin.get_autoconfig(self.configuration)
   123         autoconfig = plugin.get_autoconfig(self.configuration)
    89         
   124         
    90         # Register relations etc. to the rule engine.
   125         # Using the configuration to pass the eval globals dict to the
    91         # Due to unit test issues the relations are not registered
   126         # eval expression. The configuration only contains the globals
    92         # in the relations module, but only for the duration of
   127         # dict for the duration of the rule execution, so hopefully this
    93         # rule parsing and execution
   128         # shouldn't mess anything up
    94         relations.register()
   129         self._set_builtin_eval_globals()
    95         try:
   130         self.context._eval_expression_globals_dict = self.eval_globals
    96             # Using the configuration to pass the eval globals dict to the
   131         for i, rel in enumerate(self.relation_container):
    97             # eval expression. The configuration only contains the globals
   132             index = i + 1
    98             # dict for the duration of the rule execution, so hopefully this
   133             # Execute
    99             # shouldn't mess anything up
   134             self._execute_relation_and_log_error(rel, self.source, index, context)
   100             self._set_builtin_eval_globals()
       
   101             self.configuration._eval_expression_globals_dict = self.eval_globals
       
   102             for i, rel in enumerate(self.relation_container):
       
   103                 index = i + 1
       
   104                 
       
   105                 # Execute
       
   106                 self._execute_relation_and_log_error(rel, self.source, index)
       
   107                 
       
   108                 # Collect execution result if supported
       
   109                 if hasattr(rel, 'get_execution_result'):
       
   110                     result = rel.get_execution_result()
       
   111                     if isinstance(result, plugin.RelationExecutionResult):
       
   112                         result.source = self.source
       
   113                         result.index = index
       
   114                         results.append(result)
       
   115             
   135             
   116             del self.configuration._eval_expression_globals_dict
   136         del self.context._eval_expression_globals_dict
   117             
   137         
   118             if self.relation_container.has_errors():
   138         if self.relation_container.has_errors():
   119                 for error in self.relation_container.get_errors():
   139             for error in self.relation_container.get_errors():
   120                     self.logger.error(error)
   140                 logging.getLogger('cone.ruleml_relation_container(%s)' % self.source).error(error)
   121             
   141         
   122             if self.context:
   142         if self.context:
   123                 self.context.results += results
   143             self.context.results += results
   124             return results
   144         return results
   125         finally:
       
   126             relations.unregister()
       
   127     
   145     
   128     def get_relation_count(self):
   146     def get_relation_count(self):
   129         return len(self.relation_container)
   147         return len(self.relation_container)
   130     
   148     
       
   149     def get_relations(self):
       
   150         return list(self.relation_container)
       
   151 
   131     def _set_builtin_eval_globals(self):
   152     def _set_builtin_eval_globals(self):
   132         """
   153         """
   133         Add built-in attributes into the eval globals dictionary.
   154         Add built-in attributes into the eval globals dictionary.
   134         """
   155         """
   135         class RuleBuiltinsModule(object):
   156         builtins = RuleBuiltinsModule() 
   136             pass
       
   137                 
       
   138         builtins = RuleBuiltinsModule()
       
   139         builtins.configuration = self.configuration
   157         builtins.configuration = self.configuration
       
   158         builtins.context       = self.context
   140         
   159         
   141         self.eval_globals['ruleml'] = builtins
   160         self.eval_globals['ruleml'] = builtins
   142 
   161 
   143 class RuleImplReaderBase(plugin.ReaderBase):
   162 class RuleImplReader(plugin.ReaderBase):
   144     NAMESPACE = None # Used as a base class, so should have no namespace
   163     NAMESPACE = 'http://www.s60.com/xml/ruleml/3'
       
   164     NAMESPACE_ID = 'ruleml3'
       
   165     ROOT_ELEMENT_NAME = 'ruleml'
   145     FILE_EXTENSIONS = ['ruleml']
   166     FILE_EXTENSIONS = ['ruleml']
   146     
   167     
   147     def __init__(self, resource_ref, configuration):
   168     def __init__(self, resource_ref, configuration):
   148         self.resource_ref = resource_ref
   169         self.resource_ref = resource_ref
   149         self.configuration = configuration
   170         self.configuration = configuration
   150         self.logger = logging.getLogger('cone.ruleml(%s)' % self.resource_ref)
       
   151     
   171     
   152     @classmethod
   172     @classmethod
   153     def read_impl(cls, resource_ref, configuration, etree):
   173     def read_impl(cls, resource_ref, configuration, etree):
   154         reader = cls(resource_ref, configuration)
   174         reader = cls(resource_ref, configuration)
   155         
   175         rules = reader.parse_rules(resource_ref, etree)
   156         # Register relations etc. to the rule engine.
   176         eval_globals = reader.parse_eval_globals(etree)
   157         # Due to unit test issues the relations are not registered
   177         lineno = utils.etree.get_lineno(etree)
   158         # in the relations module, but only for the duration of
   178         
   159         # rule parsing and execution
   179         # Create an ImplContainer to hold each rule as its own
   160         relations.register()
   180         # RuleML implementation
   161         try:
   181         main_impl = plugin.ImplContainer(resource_ref, configuration)
   162             rules = reader.parse_rules(etree)
   182         main_impl.lineno = lineno
   163             eval_globals = reader.parse_eval_globals(etree)
   183         
   164             
   184         for rule in rules:
   165             relation_container = RulemlRelationContainer(
   185             relation_container = RulemlRelationContainer(
   166                 configuration   = configuration,
   186                 configuration   = configuration,
   167                 source          = resource_ref,
   187                 source          = "%s:%d" % (resource_ref, rule.lineno),
   168                 rule_list       = rules,
   188                 rule_list       = [rule],
   169                 eval_globals    = eval_globals)
   189                 eval_globals    = eval_globals)
       
   190         
       
   191             impl = RuleImpl(resource_ref, configuration, relation_container)
       
   192             impl.lineno = rule.lineno
       
   193             rule.implml = impl
   170             
   194             
   171             impl = RuleImpl(resource_ref, configuration, relation_container)
   195             main_impl.append(impl)
   172         finally:
   196         return main_impl
   173             relations.unregister()
   197     
   174         
   198     @classmethod
   175         return impl
   199     def get_schema_data(cls):
   176         
   200         return pkg_resources.resource_string('ruleplugin', 'xsd/ruleml3.xsd')
   177 class RuleImplReader1(RuleImplReaderBase):
   201     
   178     NAMESPACE = 'http://www.s60.com/xml/ruleml/1'
   202     def parse_rules(self, ref, etree):
   179     
       
   180     def __init__(self, resource_ref, configuration):
       
   181         RuleImplReaderBase.__init__(self, resource_ref, configuration)
       
   182     
       
   183     def parse_rules(self, etree):
       
   184         rules = []
   203         rules = []
   185         for elem in etree.getiterator("{%s}rule" % self.NAMESPACE):
   204         for elem in etree.getiterator("{%s}rule" % self.NAMESPACE):
   186             rules.extend(relations.RelationFactory.get_relations(self.configuration, elem.text))
   205             lineno = utils.etree.get_lineno(elem)
   187         return rules
   206             rule_str = self._replace_eval_blocks(elem.text or '')
   188     
   207             rels = relations.RelationFactory.get_relations(rule_str) or []
   189     def parse_eval_globals(self, etree):
   208             for rule in rels:
   190         return {}
   209                 rule.ref = ref
   191 
   210                 rule.lineno = lineno
   192 class RuleImplReader2(RuleImplReaderBase):
   211                 rules.append(rule)
   193     NAMESPACE = 'http://www.s60.com/xml/ruleml/2'
       
   194     
       
   195     def __init__(self, resource_ref, configuration):
       
   196         RuleImplReaderBase.__init__(self, resource_ref, configuration)
       
   197     
       
   198     def parse_rules(self, etree):
       
   199         rules = []
       
   200         for elem in etree.getiterator("{%s}rule" % self.NAMESPACE):
       
   201             rules.extend(relations.RelationFactory.get_relations(self.configuration, self._replace_eval_blocks(elem.text)))
       
   202         return rules
   212         return rules
   203     
   213     
   204     def parse_eval_globals(self, etree):
   214     def parse_eval_globals(self, etree):
   205         eval_globals = {}
   215         eval_globals = {}
   206         for elem in etree.getiterator("{%s}eval_globals" % self.NAMESPACE):
   216         for elem in etree.getiterator("{%s}eval_globals" % self.NAMESPACE):
   212                     # Strip surrounding whitespace, otherwise there might be Python
   222                     # Strip surrounding whitespace, otherwise there might be Python
   213                     # indentation errors
   223                     # indentation errors
   214                     text = elem.text.strip()
   224                     text = elem.text.strip()
   215                     exec(text, eval_globals)
   225                     exec(text, eval_globals)
   216                 except Exception, e:
   226                 except Exception, e:
   217                     self.logger.warning('Failed to evaluate eval_globals block, exception: %s' % (e))
   227                     logging.getLogger('cone.ruleml(%s)' % self.resource_ref).warning('Failed to evaluate eval_globals block, exception: %s' % (e))
   218         return eval_globals
   228         return eval_globals
   219     
   229     
   220     def _read_eval_globals_from_file(self, relative_path, eval_globals):
   230     def _read_eval_globals_from_file(self, relative_path, eval_globals):
   221         # Get the actual path (relative to the current implementation file)
   231         # Get the actual path (relative to the current implementation file)
   222         base_path = os.path.dirname(self.resource_ref)
   232         base_path = os.path.dirname(self.resource_ref)
   226             resource = None
   236             resource = None
   227             resource = self.configuration.get_resource(pyfile_path)
   237             resource = self.configuration.get_resource(pyfile_path)
   228             text = resource.read()
   238             text = resource.read()
   229             exec(text.replace('\r', ''), eval_globals)
   239             exec(text.replace('\r', ''), eval_globals)
   230         except Exception, e:
   240         except Exception, e:
   231             self.logger.warning('Cannot import eval file: %s. Exception: %s' % (pyfile_path, e))
   241             logging.getLogger('cone.ruleml(%s)' % self.resource_ref).warning('Cannot import eval file: %s. Exception: %s' % (pyfile_path, e))
   232         finally:
   242         finally:
   233             if resource is not None: resource.close()
   243             if resource is not None: resource.close()
   234         
   244         
   235     
   245     
   236     @classmethod
   246     @classmethod