# Copyright (c) 2004 LOGILAB S.A. (Paris, FRANCE). # http://www.logilab.fr/ -- mailto:contact@logilab.fr # # Copyright (c) 2004 DoCoMo Euro-Labs GmbH (Munich, Germany). # http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """a PyLog based knowledge database PyLog home: http://christophe.delord.free.fr/en/pylog/ :version: $Revision:$ :author: Logilab :copyright: 2004 LOGILAB S.A. (Paris, FRANCE) 2004 DoCoMo Euro-Labs GmbH (Munich, Germany) :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com """ __revision__ = '$Id: PyLogDB $' import re from pylog import Var, compile as pylog_compile from narval.public import AL_NS, url_to_file from narval.interfaces.rdf import IRDFStatement, IRDFRule from narval.elements import create_error from narval.elements.rdf import RDFStatementElement # TODO - stories # o ask narval to create a foaf (using foaf-a-matic?) # --> http://www.ldodds.com/foaf/foaf-a-matic.html DEFAULT_KB_FILE_URL = 'file:$NARVAL_HOME/data/kb.pl' DEFAULT_INPUT_RDF_KB_FILE_URL = 'file:$NARVAL_HOME/data/kb_in.rdf' FORBIDDEN_PREDICATES = ['is', 'in'] class KBError(Exception): pass def var_repr(var): """use to have a nice representation for PyLog's variables""" return var.name Var.__repr__ = var_repr def kb_file(obj=None): """return the path to the knowledge file and it's encoding :param obj: object adaptable to IURL :rtype: tuple(str, str) """ return url_to_file(obj, DEFAULT_KB_FILE_URL) def extract_predicates(context_src): """extract predicate from python code generated by pylog""" return [class_name for index, class_name in enumerate(re.split('class\ (.*)\(', context_src)) if index % 2] def complete(context, predicate, subject, p_object): """return the list of triplet matching given arguments (None may be used as wildcard for subject or/and object) """ subject = subject or Var('X') p_object = p_object or Var('Y') try: query = context[predicate](subject, p_object) except KeyError: raise KBError('No such predicate %s' % predicate) context['query'] = query exec 'iter = query()' in context for _ in context['iter']: yield str(subject), predicate, str(p_object) def add_statements(path, to_add): """add a list of IRDFStatement elements to the PyLOG kb file return a list of error messages if any """ stream = open(path, 'a+') reply = [] for stmt in to_add: stmt = IRDFStatement(stmt) if stmt.predicate in FORBIDDEN_PREDICATES: reply.append('"%s" is a forbidden predicate for kb' % stmt.predicate) else: stream.write('\n%s( \'%s\', \'%s\' ).\n' % ( stmt.predicate, stmt.subject, stmt.object)) stream.close() return reply # actions definitions start here ############################################## MOD_XML = ''' ''' % AL_NS def act_add_statements(inputs): """add a list of `IRDFStatement` elements to the knowledge base""" # check the statement is not already in the kb # act_unify return an error if not elements have been found if act_unify(inputs).has_key('error'): path, encoding = kb_file(inputs['kb']) errors = add_statements(path, inputs['stmts']) if errors: raise KBError('\n'.join(errors)) return {} MOD_XML = MOD_XML + """ %s IRDFStatement(elmt) elmt.getattr((TYPE_NS, 'name')) == 'uri:memory:kb' IURL(elmt) """ % act_add_statements.__doc__ def act_add_rule(inputs): """add an inference rule to the knowledge base""" # TODO find a way to test if rule exists - (then don't add it) path, encoding = kb_file(inputs['kb']) rawdata = open(path).read() stream = open(path, 'a+') rule = IRDFRule(inputs['rule']) stmt = rule.statement # FIXME: the rule formating won't handle constants properly lhs = '%s( %s, %s )' % ( stmt.predicate, stmt.subject, stmt.object) rhs = ', '.join(['%s( %s, %s )' % (stmt.predicate, stmt.subject, stmt.object) for stmt in rule.implies]) rule_str = '\n%s :-\n\t%s.\n' % (lhs, rhs) if rule_str not in rawdata : stream.write(rule_str) stream.close() return {} MOD_XML = MOD_XML + """ %s IRDFRule(elmt) elmt.getattr((TYPE_NS, 'name')) == 'uri:memory:kb' IURL(elmt) """ % act_add_rule.__doc__ def act_unify(inputs): """search for matching statements in the knowledge base""" path, encoding = kb_file(inputs['kb']) stream = open(path) context = {} exec "from pylog import *" in context exec pylog_compile(stream.read()) in context stream.close() result = [] for stmt in inputs['stmts']: stmt = IRDFStatement(stmt) try: for stmt_def in complete(context, stmt.predicate, stmt.subject, stmt.object): result.append(RDFStatementElement(*stmt_def)) except KBError: continue if not result: return {'error': create_error('no statements matching %s' % stmt, type='kb')} return {'stmts': result} MOD_XML = MOD_XML + """ %s IRDFStatement(elmt) elmt.getattr((TYPE_NS, 'name')) == 'uri:memory:kb' IURL(elmt) IRDFStatement(elmt) """ % act_unify.__doc__ def act_export(inputs): """export all statements in the knowledge base""" path, encoding = kb_file(inputs['kb']) stream = open(path) context = {} context_src = pylog_compile(stream.read()) stream.close() exec "from pylog import *" in context exec context_src in context result = [] for x in extract_predicates(context_src): for stmt_def in complete(context, x, None, None): result.append(RDFStatementElement(*stmt_def)) return {'stmts': result} MOD_XML = MOD_XML + """ %s elmt.getattr((TYPE_NS, 'name')) == 'uri:memory:kb' IURL(elmt) IRDFStatement(elmt) """ % act_export.__doc__ MOD_XML += ""