Revision: 2239
Author: jpalanca
Date: Tue Jul 10 16:56:02 2012
Log: (1) unittests now run their own platform.
(2) unittests moved to the test directory.
(3) support for session objects in WUI. Use self.session object in
controllers.
(4) login and logout functions in web user interface.
(5) support for new KB interfaces: XSB, Flora2, SPARQL, ECLiPSe and SWI.
Thanks to Markus Schatten.
http://code.google.com/p/spade2/source/detail?r=2239
Added:
/trunk/spade/ECLiPSeKB.py
/trunk/spade/Flora2KB.py
/trunk/spade/SPARQLKB.py
/trunk/spade/SWIKB.py
/trunk/spade/XSBKB.py
/trunk/spade/pyxf.py
/trunk/test.py
Deleted:
/trunk/examples/unittests
Modified:
/trunk/ChangeLog
/trunk/runspade.py
/trunk/spade/AMS.py
/trunk/spade/Agent.py
/trunk/spade/DF.py
/trunk/spade/Platform.py
/trunk/spade/SpadeConfigParser.py
/trunk/spade/bdi.py
/trunk/spade/logic.py
/trunk/spade/spade_backend.py
/trunk/spade/wui.py
/trunk/templates/header.pyra
/trunk/templates/login.pyra
/trunk/tlslite/utils/hmac.py
/trunk/xmppd/modules/__init__.py
/trunk/xmppd/modules/router.py
=======================================
--- /dev/null
+++ /trunk/spade/ECLiPSeKB.py Tue Jul 10 16:56:02 2012
@@ -0,0 +1,48 @@
+from logic import KB
+from pyxf import eclipse
+
+class ECLiPSeKB( KB ):
+ '''ECLiPSe Prolog knowledge base'''
+ def __init__( self, sentence=None, path='eclipse' ):
+ '''Constructor method
+ Usage: ECLiPSeKB( sentence, path )
+ sentence - Prolog sentence to be added to the KB (default: None)
+ path - path to ECLiPSe Prolog executable (default: 'eclipse')'''
+ self.eclipse = eclipse( path )
+ if sentence:
+ self.tell( sentence )
+
+ def tell( self, sentence ):
+ '''Adds sentence to KB'''
+ sentence = sentence.strip()
+ if sentence[ -1 ] == '.':
+ sentence = sentence[ :-1 ]
+ return self.eclipse.query( 'assert(' + sentence + ')' )
+
+ def ask( self, query ):
+ '''Queries the KB'''
+ return self.eclipse.query( query )
+
+ def retract( self, sentence ):
+ '''Deletes sentence from KB'''
+ sentence = sentence.strip()
+ if sentence[ -1 ] == '.':
+ sentence = sentence[ :-1 ]
+ return self.eclipse.query( 'retract(' + sentence + ')' )
+
+ def loadModule( self, module ):
+ '''Loads module to KB
+ Usage: instance.loadModule( path )
+ path - path to module'''
+ self.eclipse.load( module )
+
+if __name__ == '__main__':
+ kb = ECLiPSeKB()
+ kb.tell( 'a(b,c)' )
+ kb.tell( 'a(c,d)' )
+ kb.tell( '( p(_X,_Y) :- a(_X,_Y) )' )
+ kb.tell( '( p(_X,_Y) :- a(_X,_Z), p(_Z,_Y) )' )
+ for result in kb.ask( 'p(X,Y)' ):
+ print result
+ kb.retract( 'a(b,c)' )
+
=======================================
--- /dev/null
+++ /trunk/spade/Flora2KB.py Tue Jul 10 16:56:02 2012
@@ -0,0 +1,54 @@
+from logic import KB
+from pyxf import flora2
+
+class Flora2KB( KB ):
+ '''Flora2 knowledge base'''
+ def __init__( self, sentence=None, path='runflora' ):
+ '''Constructor method
+ Usage: Flora2KB( sentence, path )
+ sentence - F-logic sentence to be added to the KB (default: None)
+ path - path to Flora2 executable (default: 'runflora')'''
+ self.flora2 = flora2( path )
+ if sentence:
+ self.tell( sentence )
+
+ def tell( self, sentence, type='insert' ):
+ '''Adds sentence to KB
+ Usage: instance.tell( sentence, type )
+ sentence - frame logic sentence to be added to KB
+ type - insertion type (one of insert, insertall, t_insert,
t_insertall, insertrule, newmodule; default: 'insert')'''
+ sentence = sentence.strip()
+ if sentence[ -1 ] == '.':
+ sentence = sentence[ :-1 ]
+ return self.flora2.query( type + '{' + sentence + '}' )
+
+ def ask( self, query ):
+ '''Queries the KB'''
+ return self.flora2.query( query )
+
+ def retract( self, sentence, type='delete' ):
+ '''Deletes sentence from KB
+ Usage: instance.retract( sentence, type )
+ sentence - frame logic sentence to be deleted from KB
+ type - deletion type (one of delete, deleteall, erase, eraseall,
t_delete, t_deleteall, t_erase, t_eraseall, deletetrule, erasemodule;
default: 'delete')'''
+ sentence = sentence.strip()
+ if sentence[ -1 ] == '.':
+ sentence = sentence[ :-1 ]
+ return self.flora2.query( type + '{' + sentence + '}' )
+
+ def loadModule( self, module ):
+ '''Loads module to KB
+ Usage: instance.loadModule( path )
+ path - path to module'''
+ self.flora2.load( module )
+
+if __name__ == '__main__':
+ kb = Flora2KB()
+ kb.tell( 'a[ b->c ]' )
+ kb.tell( '( ?x[ c->?y ] :- ?x[ b->?y ] )', 'insertrule' )
+ for result in kb.ask( '?x[ ?y->?z ]' ):
+ print result
+ kb.retract( 'a[ b->c ]' )
+
+
+
=======================================
--- /dev/null
+++ /trunk/spade/SPARQLKB.py Tue Jul 10 16:56:02 2012
@@ -0,0 +1,60 @@
+from logic import KB
+from SPARQLWrapper import SPARQLWrapper, JSON, XML
+from exceptions import NotImplementedError
+
+class SPARQLKB( KB ):
+ '''SPARQL endpoint knowledge base'''
+ def __init__( self, sentence=None, endpoint=None ):
+ '''Constructor method
+ Usage:
+ sentence - RDF triple to be added to KB (not implemented; default:None)
+ endpoint - URL of endpoint to query'''
+ self.sparql = SPARQLWrapper( endpoint )
+ if sentence:
+ self.tell( sentence )
+
+ def tell( self, sentence ):
+ '''Adding triples to RDF store - not implemented'''
+ raise NotImplementedError, 'Adding sentences to RDF knowledge bases is
not implemented'
+
+ def ask( self, query ):
+ '''Queries the endpoint'''
+ self.sparql.setQuery( query )
+ self.sparql.setReturnFormat( XML )
+ res = self.sparql.query().convert().getElementsByTagName( 'result' )
+ res = [ dict( [ ( bin.attributes[ 'name' ].nodeValue,
bin.firstChild.firstChild.nodeValue ) for bin in
node.getElementsByTagName( 'binding' ) ] ) for node in res ]
+ return res
+
+
+ def retract( self, sentence ):
+ '''Removing triples from RDF store - not implemented'''
+ raise NotImplementedError, 'Removing sentences to RDF knowledge bases
is not implemented'
+
+
+if __name__ == '__main__':
+ s = SPARQLKB( endpoint='
http://lod.openlinksw.com/sparql' )
+ for result in s.ask( '''
+PREFIX gr:<
http://purl.org/goodrelations/v1#>
+PREFIX rdfs: <
http://www.w3.org/2000/01/rdf-schema#>
+
+SELECT ?l ?p ?c ?s
+WHERE {
+?s a gr:Offering.
+?s rdfs:label ?l.
+?s gr:hasPriceSpecification ?ps.
+?ps gr:hasCurrencyValue ?p.
+?ps gr:hasCurrency ?c
+FILTER( regex( ?l, "yoghurt", "i" ) )
+} ORDER BY ?p LIMIT 500 ''' ):
+ print result[ 'l' ], result[ 'p' ], result[ 'c' ]
+
+ s = SPARQLKB( endpoint='
http://dbpedia.org/sparql' )
+ for result in s.ask( '''
+ PREFIX rdfs: <
http://www.w3.org/2000/01/rdf-schema#>
+ SELECT ?label
+ WHERE {
+ <
http://dbpedia.org/resource/Croatia>
+ rdfs:label
+ ?label
+ }''' ):
+ print result[ 'label' ]
=======================================
--- /dev/null
+++ /trunk/spade/SWIKB.py Tue Jul 10 16:56:02 2012
@@ -0,0 +1,48 @@
+from logic import KB
+from pyxf import swipl
+
+class SWIKB( KB ):
+ '''SWI Prolog knowledge base'''
+ def __init__( self, sentence=None, path='swipl' ):
+ '''Constructor method
+ Usage: SWIKB( sentence, path )
+ sentence - Prolog sentence to be added to the KB (default: None)
+ path - path to SWI Prolog executable (default: 'swipl')'''
+ self.swi = swipl( path )
+ if sentence:
+ self.tell( sentence )
+
+ def tell( self, sentence ):
+ '''Adds sentence to KB'''
+ sentence = sentence.strip()
+ if sentence[ -1 ] == '.':
+ sentence = sentence[ :-1 ]
+ return self.swi.query( 'assert(' + sentence + ')' )
+
+ def ask( self, query ):
+ '''Queries the KB'''
+ return self.swi.query( query )
+
+ def retract( self, sentence ):
+ '''Deletes sentence from KB'''
+ sentence = sentence.strip()
+ if sentence[ -1 ] == '.':
+ sentence = sentence[ :-1 ]
+ return self.swi.query( 'retract(' + sentence + ')' )
+
+ def loadModule( self, module ):
+ '''Loads module to KB
+ Usage: instance.loadModule( path )
+ path - path to module'''
+ self.swi.load( module )
+
+if __name__ == '__main__':
+ kb = SWIKB()
+ kb.tell( 'a(b,c)' )
+ kb.tell( 'a(c,d)' )
+ kb.tell( '( p(_X,_Y) :- a(_X,_Y) )' )
+ kb.tell( '( p(_X,_Y) :- a(_X,_Z), p(_Z,_Y) )' )
+ for result in kb.ask( 'p(X,Y)' ):
+ print result
+ kb.retract( 'a(b,c)' )
+
=======================================
--- /dev/null
+++ /trunk/spade/XSBKB.py Tue Jul 10 16:56:02 2012
@@ -0,0 +1,48 @@
+from logic import KB
+from pyxf import xsb
+
+class XSBKB( KB ):
+ '''XSB Prolog knowledge base'''
+ def __init__( self, sentence=None, path='xsb' ):
+ '''Constructor method
+ Usage: XSBKB( sentence, path )
+ sentence - Prolog sentence to be added to the KB (default: None)
+ path - path to XSB executable (default: 'xsb')'''
+ self.xsb = xsb( path )
+ if sentence:
+ self.tell( sentence )
+
+ def tell( self, sentence ):
+ '''Adds sentence to KB'''
+ sentence = sentence.strip()
+ if sentence[ -1 ] == '.':
+ sentence = sentence[ :-1 ]
+ return self.xsb.query( 'assert(' + sentence + ')' )
+
+ def ask( self, query ):
+ '''Queries the KB'''
+ return self.xsb.query( query )
+
+ def retract( self, sentence ):
+ '''Deletes sentence from KB'''
+ sentence = sentence.strip()
+ if sentence[ -1 ] == '.':
+ sentence = sentence[ :-1 ]
+ return self.xsb.query( 'retract(' + sentence + ')' )
+
+ def loadModule( self, module ):
+ '''Loads module to KB
+ Usage: instance.loadModule( path )
+ path - path to module'''
+ self.xsb.load( module )
+
+if __name__ == '__main__':
+ kb = XSBKB()
+ kb.tell( 'a(b,c)' )
+ kb.tell( 'a(c,d)' )
+ kb.tell( '( p(_X,_Y) :- a(_X,_Y) )' )
+ kb.tell( '( p(_X,_Y) :- a(_X,_Z), p(_Z,_Y) )' )
+ for result in kb.ask( 'p(X,Y)' ):
+ print result
+ kb.retract( 'a(b,c)' )
+
=======================================
--- /dev/null
+++ /trunk/spade/pyxf.py Tue Jul 10 16:56:02 2012
@@ -0,0 +1,464 @@
+__doc__ = ''' Python interface to XSB Prolog, SWI Prolog, ECLiPSe Prolog
and Flora2
+ by Markus Schatten <markus_dot_schatten_at_foi_dot_hr>
+ Faculty of Organization and Informatics,
+ Varazdin, Croatia, 2011
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA'''
+
+__version__ = '1.0.1'
+
+import pexpect as px
+import re
+
+xsbprompt = '[|][ ][?][-][ ]'
+xsberror = '[+][+]Error.*'
+
+var_re = re.compile( '[^a-zA-Z0-9_]([A-Z][a-zA-Z0-9_]*)' )
+res_re = re.compile( "res[\(]'([A-Z][a-zA-Z0-9_]*)',[ ]?(.*)[\)]" )
+
+class XSBExecutableNotFound( Exception ):
+ '''Exception raised if XSB executable is not found on the specified
path.'''
+ pass
+
+class XSBCompileError( Exception ):
+ '''Exception raised if loaded module has compile errors.'''
+ pass
+
+class XSBQueryError( Exception ):
+ '''Exception raised if query raises an error.'''
+ pass
+
+class xsb:
+ '''Python interface to XSB Prolog (
http://xsb.sf.net)'''
+ def __init__( self, path='xsb', args='--nobanner --quietload' ):
+ '''Constructor method
+ Usage: xsb( path, args )
+ path - path to XSB executable (default: 'xsb')
+ args - command line arguments (default: '--nobanner --quietload')
+
+ self.engine becomes pexpect spawn instance of XSB Prolog shell
+
+ Raises: XSBExecutableNotFound'''
+ try:
+ self.engine = px.spawn( path + ' ' + args, timeout=5 )
+ self.engine.expect( xsbprompt )
+ except px.ExceptionPexpect:
+ raise XSBExecutableNotFound, 'XSB executable not found on the
specified path. Try using xsb( "/path/to/XSB/bin/xsb" )'
+
+ def load( self, module ):
+ '''Loads module into self.engine
+ Usage: instance.load( path )
+ path - path to module file
+
+ Raises: XSBCompileError'''
+ self.engine.sendline( "['" + module + "']." )
+ index = self.engine.expect( [ xsbprompt, xsberror ] )
+ if index == 1:
+ raise XSBCompileError, 'Error while compiling module "' + module
+ '". Error from XSB:\n' + self.engine.after
+
+ def query( self, query ):
+ '''Queries current engine state
+ Usage: instance.query( query )
+ query - usual XSB Prolog query (example: 'likes( X, Y )')
+
+ Returns:
+ True - if yes/no query and answer is yes
+ False - if yes/no query and answer is no
+ List of dictionaries - if normal query. Dictionary keys are returned
+ variable names. Example:
+ >>> instance.query( 'likes( Person, Food )' )
+ [{'Person': 'john', 'Food': 'curry'},
{'Person': 'sandy', 'Food': 'mushrooms'}]
+
+ Raises: XSBQueryError'''
+ query = query.strip()
+ if query[ -1 ] != '.':
+ query += '.'
+ lvars = var_re.findall( query )
+ lvars = list( set( lvars ) )
+ if lvars == []: # yes/no query (no variables)
+ self.engine.sendline( query )
+ index = self.engine.expect( [ xsbprompt, xsberror ] )
+ if index == 1:
+ raise XSBQueryError, 'Error while executing query "' + query + '". Error
from XSB:\n' + self.engine.after
+ else:
+ if 'yes' in self.engine.before:
+ return True
+ else:
+ return False
+ else: # normal query
+ printer = self._printer( lvars, query )
+ self.engine.sendline( printer )
+ index = self.engine.expect( [ xsberror, xsbprompt ] )
+ if index == 0:
+ raise XSBQueryError, 'Error while executing query "' + query + '". Error
from XSB:\n' + self.engine.after
+ else:
+ res = res_re.findall( self.engine.before.split( ',nl,fail.\n' )[ -1 ] )
+ results = []
+ counter = 0
+ temp = []
+ for i in res:
+ counter += 1
+ temp.append( i )
+ if counter % len( lvars ) == 0:
+ results.append( dict( temp ) )
+ temp = []
+ if results == []:
+ return False
+ return results
+
+ def _printer( self, lvars, query ):
+ '''Private method for constructing a result printing query.
+ Usage: instance._printer( lvars, query )
+ lvars - list of logical variables to print
+ query - query containing the variables to be printed
+
+ Returns: string of the form 'query, writeln( res( 'VarName1', VarName1
) ) ... writeln( res( 'VarNameN', VarNameN ) ),nl,fail.'
+ '''
+ query = query[ :-1 ]
+ elems = [ "writeln( res('''" + i + "'''," + i + ") )" for i in lvars ]
+ printer = query + ',' + ','.join( elems ) + ',nl,fail.'
+ return printer
+
+swiprompt = '[?][-][ ]'
+swierror = 'ERROR.*'
+
+class SWIExecutableNotFound( Exception ):
+ '''Exception raised if SWI-Prolog executable is not found on the
specified path.'''
+ pass
+
+class SWICompileError( Exception ):
+ '''Exception raised if loaded module has compile errors.'''
+ pass
+
+class SWIQueryError( Exception ):
+ '''Exception raised if query raises an error.'''
+ pass
+
+class swipl:
+ '''Python interface to SWI Prolog (
http://www.swi-prolog.org)'''
+ def __init__( self, path='swipl', args='-q +tty' ):
+ '''Constructor method
+ Usage: swipl( path, args )
+ path - path to SWI executable (default: 'swipl')
+ args - command line arguments (default: '-q +tty')
+
+ self.engine becomes pexpect spawn instance of SWI Prolog shell
+
+ Raises: SWIExecutableNotFound'''
+ try:
+ self.engine = px.spawn( path + ' ' + args, timeout=5 )
+ self.engine.expect( swiprompt )
+ except px.ExceptionPexpect:
+ raise SWIExecutableNotFound, 'SWI-Prolog executable not found on the
specified path. Try installing swi-prolog or using swipl( "/path/to/swipl"
)'
+
+ def load( self, module ):
+ '''Loads module into self.engine
+ Usage: instance.load( path )
+ path - path to module file
+
+ Raises: SWICompileError'''
+ self.engine.sendline( "['" + module + "']." )
+ index = self.engine.expect( [ swierror, swiprompt ] )
+ if index == 0:
+ raise SWICompileError, 'Error while compiling module "' + module
+ '". Error from SWI:\n' + self.engine.after
+
+ def query( self, query ):
+ '''Queries current engine state
+ Usage: instance.query( query )
+ query - usual SWI Prolog query (example: 'likes( X, Y )')
+
+ Returns:
+ True - if yes/no query and answer is yes
+ False - if yes/no query and answer is no
+ List of dictionaries - if normal query. Dictionary keys are returned
+ variable names. Example:
+ >>> instance.query( 'likes( Person, Food )' )
+ [{'Person': 'john', 'Food': 'curry'},
{'Person': 'sandy', 'Food': 'mushrooms'}]
+
+ Raises: SWIQueryError'''
+ query = query.strip()
+ if query[ -1 ] != '.':
+ query += '.'
+ lvars = var_re.findall( query )
+ lvars = list( set( lvars ) )
+ if lvars == []: # yes/no query (no variables)
+ self.engine.sendline( query )
+ index = self.engine.expect( [ swiprompt, swierror ] )
+ if index == 1:
+ raise SWIQueryError, 'Error while executing query "' + query + '". Error
from SWI:\n' + self.engine.after
+ else:
+ if 'true' in self.engine.before:
+ return True
+ else:
+ return False
+ else: # normal query
+ printer = self._printer( lvars, query )
+ self.engine.sendline( printer )
+ index = self.engine.expect( [ swierror, swiprompt ] )
+ if index == 0:
+ raise SWIQueryError, 'Error while executing query "' + query + '". Error
from SWI:\n' + self.engine.after
+ else:
+ res = res_re.findall( self.engine.before.split( ',nl,fail.\n' )[ -1 ] )
+ results = []
+ counter = 0
+ temp = []
+ for i in res:
+ counter += 1
+ temp.append( i )
+ if counter % len( lvars ) == 0:
+ results.append( dict( temp ) )
+ temp = []
+ if results == []:
+ return False
+ return results
+
+ def _printer( self, lvars, query ):
+ '''Private method for constructing a result printing query.
+ Usage: instance._printer( lvars, query )
+ lvars - list of logical variables to print
+ query - query containing the variables to be printed
+
+ Returns: string of the form 'query, writeln( res( 'VarName1', VarName1
) ) ... writeln( res( 'VarNameN', VarNameN ) ),nl,fail.'
+ '''
+ query = query[ :-1 ]
+ elems = [ "writeln( res('''" + i + "'''," + i + ") )" for i in lvars ]
+ printer = query + ',' + ','.join( elems ) + ',nl,fail.'
+ return printer
+
+eclipseprompt = '[\[]eclipse [0-9]+[\]][:] '
+eclipseerror = 'Abort.*'
+
+class ECLiPSeExecutableNotFound( Exception ):
+ '''Exception raised if ECLiPSe-Prolog executable is not found on the
specified path.'''
+ pass
+
+class ECLiPSeCompileError( Exception ):
+ '''Exception raised if loaded module has compile errors.'''
+ pass
+
+class ECLiPSeQueryError( Exception ):
+ '''Exception raised if query raises an error.'''
+ pass
+
+class eclipse:
+ '''Python interface to ECLiPSe Prolog (
http://eclipseclp.org)'''
+ def __init__( self, path='eclipse', args='' ):
+ '''Constructor method
+ Usage: eclipse( path, args )
+ path - path to ECLiPSe executable (default: 'eclipse')
+ args - command line arguments (default: '')
+
+ self.engine becomes pexpect spawn instance of ECLiPSe Prolog shell
+
+ Raises: ECLiPSeExecutableNotFound'''
+ try:
+ self.engine = px.spawn( path + ' ' + args, timeout=5 )
+ except px.ExceptionPexpect:
+ raise ECLiPSeExecutableNotFound, 'ECLiPSe Prolog executable not
found on the specified path.'
+ self.engine.expect( eclipseprompt )
+
+ def load( self, module ):
+ '''Loads module into self.engine
+ Usage: instance.load( path )
+ path - path to module file
+
+ Raises: ECLiPSeCompileError'''
+ self.engine.sendline( "['" + module + "']." )
+ index = self.engine.expect( [ eclipseerror, eclipseprompt ] )
+ if index == 0:
+ raise ECLiPSeCompileError, 'Error while compiling module "' + module
+ '". Error from ECLiPSe:\n' + self.engine.after
+
+ def query( self, query ):
+ '''Queries current engine state
+ Usage: instance.query( query )
+ query - usual ECLiPSe Prolog query (example: 'likes( X, Y )')
+
+ Returns:
+ True - if yes/no query and answer is yes
+ False - if yes/no query and answer is no
+ List of dictionaries - if normal query. Dictionary keys are returned
+ variable names. Example:
+ >>> instance.query( 'likes( Person, Food )' )
+ [{'Person': 'john', 'Food': 'curry'},
{'Person': 'sandy', 'Food': 'mushrooms'}]
+
+ Raises: ECLiPSeQueryError'''
+ query = query.strip()
+ if query[ -1 ] != '.':
+ query += '.'
+ lvars = var_re.findall( query )
+ lvars = list( set( lvars ) )
+ if lvars == []: # yes/no query (no variables)
+ self.engine.sendline( query )
+ index = self.engine.expect( [ eclipseprompt, eclipseerror ] )
+ if index == 1:
+ raise ECLiPSeQueryError, 'Error while executing query "' + query + '".
Error from ECLiPSe:\n' + self.engine.after
+ else:
+ if 'Yes' in self.engine.before:
+ return True
+ else:
+ return False
+ else: # normal query
+ printer = self._printer( lvars, query )
+ self.engine.sendline( printer )
+ index = self.engine.expect( [ eclipseerror, eclipseprompt ] )
+ if index == 0:
+ raise ECLiPSeQueryError, 'Error while executing query "' + query + '".
Error from ECLiPSe:\n' + self.engine.after
+ else:
+ res = res_re.findall( self.engine.before.split( ',nl,fail.\n' )[ -1 ] )
+ results = []
+ counter = 0
+ temp = []
+ for i in res:
+ counter += 1
+ temp.append( i )
+ if counter % len( lvars ) == 0:
+ results.append( dict( temp ) )
+ temp = []
+ if results == []:
+ return False
+ return results
+
+ def _printer( self, lvars, query ):
+ '''Private method for constructing a result printing query.
+ Usage: instance._printer( lvars, query )
+ lvars - list of logical variables to print
+ query - query containing the variables to be printed
+
+ Returns: string of the form 'query, writeln( res( 'VarName1', VarName1
) ) ... writeln( res( 'VarNameN', VarNameN ) ),nl,fail.'
+ '''
+ query = query[ :-1 ]
+ elems = [ "writeln( res('\\'" + i + "\\''," + i + ") )" for i in lvars
]
+ printer = query + ',' + ','.join( elems ) + ',nl,fail.'
+ return printer
+
+flora2prompt = 'flora2 [?][-][ ]'
+flora2error = '[+][+]Error.*'
+
+fvar_re = re.compile( '[?][a-zA-Z0-9][a-zA-Z0-9_]*' )
+fres_re = re.compile( "[?]([a-zA-Z0-9_]*) [=] ([^\r]+)" )
+
+
+class Flora2ExecutableNotFound( Exception ):
+ '''Exception raised if Flora2 executable is not found on the specified
path.'''
+ pass
+
+class Flora2CompileError( Exception ):
+ '''Exception raised if loaded module has compile errors.'''
+ pass
+
+class Flora2QueryError( Exception ):
+ '''Exception raised if query raises an error.'''
+ pass
+
+class flora2:
+ '''Python interface to Flora2 (
http://flora.sf.net)'''
+ def __init__( self, path='runflora', args='--nobanner --quietload' ):
+ '''Constructor method
+ Usage: flora2( path, args )
+ path - path to Flora2 executable (default: 'runflora')
+ args - command line arguments (default: '--nobanner --quietload')
+
+ self.engine becomes pexpect spawn instance of Flora2 shell
+
+ Raises: SWIExecutableNotFound'''
+ try:
+ self.engine = px.spawn( path + ' ' + args, timeout=5 )
+ self.engine.expect( flora2prompt )
+ self.engine.expect( flora2prompt )
+ except px.ExceptionPexpect:
+ raise SWIExecutableNotFound, 'Flora-2 executable not found on the
specified path. Try using flora2( "/path/to/flora2/runflora" )'
+
+ def load( self, module ):
+ '''Loads module into self.engine
+ Usage: instance.load( path )
+ path - path to module file
+
+ Raises: Flora2CompileError'''
+ self.engine.sendline( "['" + module + "']." )
+ index = self.engine.expect( [ flora2prompt, flora2error ] )
+ if index == 1:
+ raise Flora2CompileError, 'Error while compiling module "' + module
+ '". Error from Flora2:\n' + self.engine.after
+
+ def query( self, query ):
+ '''Queries current engine state
+ Usage: instance.query( query )
+ query - usual Flora2 query (example: '?x[ likes->?y ]')
+
+ Returns:
+ True - if yes/no query and answer is yes
+ False - if yes/no query and answer is no
+ List of dictionaries - if normal query. Dictionary keys are returned
+ variable names. Example:
+ >>> instance.query( '?person[ likes->?food ]' )
+ [{'person': 'john', 'food': 'curry'},
{'person': 'sandy', 'food': 'mushrooms'}]
+
+ Raises: Flora2QueryError'''
+ query = query.strip()
+ if query[ -1 ] != '.':
+ query += '.'
+ lvars = fvar_re.findall( query )
+ lvars = list( set( lvars ) )
+ if lvars == []: # yes/no query (no variables)
+ self.engine.sendline( query )
+ index = self.engine.expect( [ flora2prompt, flora2error ] )
+ if index == 1:
+ raise Flora2QueryError, 'Error while executing query "' + query + '".
Error from Flora2:\n' + self.engine.after
+ else:
+ if 'Yes' in self.engine.before:
+ return True
+ else:
+ return False
+ else: # normal query
+ self.engine.sendline( query )
+ index = self.engine.expect( [ flora2error, flora2prompt ] )
+ if index == 0:
+ raise Flora2QueryError, 'Error while executing query "' + query + '".
Error from Flora2:\n' + self.engine.after
+ else:
+ res = fres_re.findall( self.engine.before )
+ results = []
+ counter = 0
+ temp = []
+ for i in res:
+ counter += 1
+ temp.append( i )
+ if counter % len( lvars ) == 0:
+ results.append( dict( temp ) )
+ temp = []
+ return results
+
+if __name__ == '__main__':
+ x = xsb()
+ x.load( 'test_xsb' )
+ print x.query( 'dislikes( john, mushrooms )' )
+ print x.query( 'likes( Person, Food )' )
+ del x
+
+ s = swipl()
+ s.load( 'test_swi' )
+ print s.query( 'dislikes( john, mushrooms )' )
+ print s.query( 'likes( Person, Food )' )
+ del s
+
+ e = eclipse()
+ e.load( 'test_eclipse' )
+ print e.query( 'dislikes( john, mushrooms )' )
+ print e.query( 'likes( Person, Food )' )
+ del e
+
+ f = flora2()
+ f.load( 'test_flora' )
+ print f.query( 'john[ dislikes->mushrooms ]' )
+ print f.query( '?person[ likes->?food ]' )
+ del f
=======================================
--- /dev/null
+++ /trunk/test.py Tue Jul 10 16:56:02 2012
@@ -0,0 +1,42 @@
+from test.basicTestCase import *
+from test.amsTestCase import *
+from test.dfTestCase import *
+from test.coTestCase import *
+from test.p2pTestCase import *
+from test.rpcTestCase import *
+from test.aidTestCase import *
+from test.dadTestCase import *
+from test.eventbehavTestCase import *
+from test.pubsubTestCase import *
+from test.kbTestCase import *
+
+from spade import spade_backend
+from xmppd.xmppd import Server
+import thread
+
+import os
+d="test"+os.sep
+
+s = Server(cfgfile=d+"unittests_xmppd.xml",
cmd_options={'enable_debug':[], 'enable_psyco':False})
+thread.start_new_thread(s.run,tuple())
+
+platform = spade_backend.SpadeBackend(d+"unittests_spade.xml")
+platform.start()
+
+
+
+try:
+ import xmlrunner
+ use_xmlrunner = True
+except:
+ use_xmlrunner = False
+
+if use_xmlrunner:
+ unittest.main(testRunner=xmlrunner.XMLTestRunner(output='test-reports'))
+else:
+ print "XMLTestRunner not found."
+ unittest.main()
+
+platform.shutdown()
+s.shutdown("Jabber server terminated...")
+
=======================================
--- /trunk/ChangeLog Wed Jul 14 06:10:54 2010
+++ /trunk/ChangeLog Tue Jul 10 16:56:02 2012
@@ -1,3 +1,107 @@
+2012-06-01 18:05 jpalanca
+
+ * trunk/examples/unittests/coverage-unittests.py: added unittests
+ for coverage analysis
+
+2012-06-01 16:52 jpalanca
+
+ * trunk/examples/unittests/unittests.py: added
+ unittest-xml-reporting
+
+2011-11-28 14:37 jpalanca
+
+ * ChangeLog, doc/manual/book.xml, doc/manual/ch03-basic-agents.xml,
+ doc/manual/ch06-bdi.xml, doc/manual/html/spade.bdi.html,
+ doc/manual/images/note.png, doc/manual/images/tip.png,
+ doc/manual/images/warning.png, svn2cl, svn2cl.xsl,
+ trunk/spade/Agent.py: -Solved some issues in documentation
+
+2011-11-16 16:33 jpalanca
+
+ * trunk/spade/Agent.py, trunk/spade/Behaviour.py: -Added
+ retro-compatibility support for FIPA messages. Use the method
+ attribute "xmppfipa" in the send function. -Added the regex patch
+ to the message templates. Thanks to J. fernando Sanchez
+
+2011-11-11 16:02 jpalanca
+
+ * trunk/spade/Agent.py, trunk/spade/diagram.py,
+ trunk/templates/messages.pyra: -Changed sequencediagrams to
+ Javascript API. Old API was not working. Still need to improve it.
+ Websequencediagrams.com does not support large diagrams.
+
+2010-07-30 13:42 jpalanca
+
+ * trunk/examples/unittests/aidTestCase.py,
+ trunk/examples/unittests/dadTestCase.py,
+ trunk/examples/unittests/dfTestCase.py,
+ trunk/examples/unittests/pubsubTestCase.py,
+ trunk/examples/unittests/rpcTestCase.py, trunk/spade/AID.py,
+ trunk/spade/Agent.py, trunk/spade/DF.py, trunk/spade/RPC.py,
+ trunk/spade/pubsub.py, trunk/xmppd/modules/pubsub.py: - Modified
+ RPC API. Now service P and Q are matched with inputs and outputs
+ stored (optionally) in the agent KB - DF.Service class now has a
+ asContentObject method, so it can be converted easy to a XML
+ string - DF now publishes service registrations and
+ unregistrations using pubsub events - Fixed some XML and CO
+ generations - Fixed pubsub module. Now PlatformAgents (with
+ namespace NS_COMPONENT_ACCEPT) can use pubsub. Fixes issue 60.
+
+2010-07-20 15:35 jpalanca
+
+ * trunk/examples/unittests/pubsubTestCase.py, trunk/spade/Agent.py,
+ trunk/spade/Behaviour.py: - Added EventBehaviour to the pubsub
+ subscriptions. Now an agent can register a callback
+ (EventBehaviour) when an event is received! - Added and improved
+ more pubsub unittests.
+
+2010-07-16 17:41 jpalanca
+
+ * trunk/examples/pubsub, trunk/examples/pubsub/juliet.py,
+ trunk/examples/pubsub/romeo.py,
+ trunk/examples/unittests/pubsubTestCase.py,
+ trunk/examples/unittests/unittests.py, trunk/spade.sh,
+ trunk/spade/Agent.py, trunk/spade/pubsub.py,
+ trunk/spade/socialnetwork.py, trunk/xmpp/protocol.py,
+ trunk/xmppd/modules/__init__.py, trunk/xmppd/modules/db_fake.py,
+ trunk/xmppd/modules/pubsub.py: - Added Personal Event Publication
+ and Subscription Protocol! This amazing new feature (developed by
+ cooldwind and sangarb1) allows agents to create events and
+ subscribe to friend events, when their contact publish events.
+ This was done following the Jabber XEP #60. This is an inital
+ version, in future work more features will be added to the pubsub
+ protocol. - Pubsub unittest were added. There is still some work
+ to check their reliability. - Also some pubsub examples in
+ directory examples/pubsub - Old spade.sh was deleted.
+
+2010-07-16 10:06 jpalanca
+
+ * trunk/runspade.py, trunk/tlslite/TLSRecordLayer.py,
+ trunk/tlslite/mathtls.py, trunk/tlslite/messages.py,
+ trunk/tlslite/utils/cryptomath.py, trunk/xmpp/auth.py,
+ trunk/xmppd/modules/dialback.py, trunk/xmppd/modules/jep0078.py,
+ trunk/xmppd/modules/stream.py, trunk/xmppd/xmppd.py: - xmppd and
+ tlslite now use hashlib instead of md5 and sha. Fixes issue 52 -
+ runspade checks for netbsd OS
+
+2010-07-14 13:10 jpalanca
+
+ * trunk/ChangeLog, trunk/examples/unittests/dfTestCase.py,
+ trunk/spade/ACLMessage.py, trunk/spade/AMS.py,
+ trunk/spade/Agent.py, trunk/spade/Behaviour.py, trunk/spade/DF.py,
+ trunk/spade/Platform.py, trunk/spade/fipa.py, trunk/spade/wui.py,
+ trunk/svn2cl, trunk/templates/404.pyra, trunk/templates/501.pyra,
+ trunk/templates/503.pyra, trunk/templates/agents.pyra,
+ trunk/templates/message.pyra, trunk/templates/sentmsg.pyra,
+ trunk/xmpp/protocol.py: - Added send message WUI page - Fixed
+ not-acceptable protocol type - Added reply_with and in_reply_to to
+ ams and df templates to allow agents sending messages to
+ themselves - Fixed ACL template match algorithm - Fixed acc loop
+ hole when sending a message to the acc agent - DF main behaviour
+ is not the default behaviour. using templates - WUI: notifyAMS now
+ uses the standard fipa modifyAgent procedure - Error templates use
+ new CSS style
+
2010-07-11 23:13 jpalanca
* trunk/examples/unittests/aidTestCase.py,
=======================================
--- /trunk/runspade.py Fri Jul 16 03:06:02 2010
+++ /trunk/runspade.py Tue Jul 10 16:56:02 2012
@@ -112,10 +112,10 @@
path = "/usr/pkg/lib" + os.sep + pyvers + "/site-packages/xmppd/"
os.chdir(path)
else:
- os.chdir("xmppd")
+ pass #os.chdir("xmppd")
s = Server(cfgfile=jabberxml,
cmd_options={'enable_debug':dbg, 'enable_psyco':False})
- os.chdir("..")
+ #os.chdir("..")
sys.stdout.write(".")
sys.stdout.flush()
=======================================
--- /trunk/spade/AMS.py Wed Jul 14 06:10:54 2010
+++ /trunk/spade/AMS.py Tue Jul 10 16:56:02 2012
@@ -580,8 +580,8 @@
return -1
- def __init__(self,node,passw,server="localhost",port=5347):
- PlatformAgent.__init__(self,node,passw,server,port)
+ def __init__(self,node,passw,server="localhost",port=5347,config={}):
+ PlatformAgent.__init__(self,node,passw,server,port,config)
def _setup(self):
=======================================
--- /trunk/spade/Agent.py Mon Nov 28 06:37:01 2011
+++ /trunk/spade/Agent.py Tue Jul 10 16:56:02 2012
@@ -70,6 +70,20 @@
threading.stack_size(64 * 1024) # 64k compo
except: pass
+
+def require_login(func):
+ self = func.__class__
+ def wrap(self, *args, **kwargs):
+ if (not hasattr(self.session,"user_authenticated") or
getattr(self.session,"user_authenticated")==False) and
self.wui.passwd!=None:
+ name = self.getName().split(".")[0].upper()
+ if name=="ACC": name="SPADE"
+ return "login.pyra",
{"name":name,'message':"Authentication is
required.", "forward_url":self.session.url}
+ return func(self,*args,**kwargs)
+ wrap.__doc__=func.__doc__
+ wrap.__name__=func.__name__
+ return wrap
+
+
class AbstractAgent(MessageReceiver.MessageReceiver):
"""
Abstract Agent
@@ -105,12 +119,17 @@
self._messages_mutex = thread.allocate_lock()
self.wui = WUI(self)
+ self.wui.registerController("index",self.WUIController_admin)
+ self.wui.registerController("login",self.WUIController_login)
+ self.wui.registerController("logout",self.WUIController_logout)
self.wui.registerController("admin", self.WUIController_admin)
self.wui.registerController("log", self.WUIController_log)
self.wui.registerController("messages",self.WUIController_messages)
self.wui.registerController("search",self.WUIController_search)
self.wui.registerController("send",self.WUIController_sendmsg)
self.wui.registerController("sent",self.WUIController_sent)
+ self.wui.passwd = None
+
self._aclparser = ACLParser.ACLxmlParser()
#self._friend_list = [] # Legacy
@@ -157,6 +176,31 @@
#BDI Support
self.KB = {} #Knowledge Base
+ def setAdminPasswd(self, passwd):
+ self.wui.passwd = str(passwd)
+
+ def WUIController_login(self, password=None, forward_url="index"):
+ if hasattr(self.session, "user_authenticated") and
getattr(self.session,"user_authenticated")==True:
+ raise HTTP_REDIRECTION, 'index'
+
+ name = self.getName().split(".")[0].upper()
+ if name=="ACC": name="SPADE"
+
+ if password==None:
+ return "login.pyra", {"name":name, "message":"Authentication is
required.","forward_url":forward_url}
+ if password!=self.wui.passwd:
+ return "login.pyra", {"name":name, "message":"Password is incorrect.
Try again.","forward_url":forward_url}
+
+ else:
+ setattr(self.session,"user_authenticated",True)
+ raise HTTP_REDIRECTION, forward_url
+
+ def WUIController_logout(self):
+ if hasattr(self.session, "user_authenticated"):
+ delattr(self.session,"user_authenticated")
+ raise HTTP_REDIRECTION, "index"
+
+ @require_login
def WUIController_admin(self):
import types
behavs = {}
@@ -170,15 +214,17 @@
attrs[attribute] = eval( "str(self."+attribute+")" )
sorted_attrs = attrs.keys()
sorted_attrs.sort()
- import pygooglechart
- chart=pygooglechart.QRChart(125,125)
- chart.add_data(self.getAID().asXML())
- chart.set_ec('H',0)
+ import pygooglechart
+ chart=pygooglechart.QRChart(125,125)
+ chart.add_data(self.getAID().asXML())
+ chart.set_ec('H',0)
return "admin.pyra",
{"name":self.getName(),"aid":self.getAID(), "qrcode":chart.get_url(), "defbehav":(id(self._defaultbehaviour),self._defaultbehaviour), "behavs":behavs, "p2pready":self.p2p_ready, "p2proutes":self.p2p_routes, "attrs":attrs, "sorted_attrs":sorted_attrs}
+ @require_login
def WUIController_log(self):
return "log.pyra", {"name":self.getName(), "log":self.getLog()}
+ @require_login
def WUIController_messages(self,agents=None):
index=0
mess = {}
@@ -249,6 +295,7 @@
return "messages.pyra",
{"name":self.getName(), "messages":mess, "diagram":
msc, "agentslist":agentslist}
+ @require_login
def WUIController_search(self, query):
#FIRST SEARCH AGENTS
@@ -371,6 +418,7 @@
return "search.pyra", {"name":self.getName(), "agentslist":
agentslist, "awuis":awuis, "services":servs}
+ @require_login
def WUIController_sendmsg(self, to=None):
from AMS import AmsAgentDescription
agentslist = []
@@ -381,6 +429,7 @@
agentslist.append(a.getAID().getName())
return "message.pyra",
{"name":self.getName(), "keys":agentslist, "to":to}
+ @require_login
def WUIController_sent(self,
receivers=[],performative=None,sender=None,reply_with=None,reply_by=None,reply_to=None,in_reply_to=None,encoding=None,language=None,ontology=None,protocol=None,conversation_id=None,content=""):
msg = ACLMessage.ACLMessage()
import types
@@ -1596,6 +1645,7 @@
def __init__(self, node, password, server="localhost", port=5347,
config=None ,debug = [], p2p=False):
AbstractAgent.__init__(self, node, server, p2p=p2p)
self.config = config
+ if config.has_key('adminpasswd'): self.wui.passwd =
config['adminpasswd']
self.debug = debug
self.jabber = xmpp.Component(server=server, port=port,
debug=self.debug)
if not self._register(password):
=======================================
--- /trunk/spade/DF.py Fri Jul 30 06:42:53 2010
+++ /trunk/spade/DF.py Tue Jul 10 16:56:02 2012
@@ -737,8 +737,8 @@
return -1
- def __init__(self,node,passw,server="localhost",port=5347):
- PlatformAgent.__init__(self,node,passw,server,port)
+ def __init__(self,node,passw,server="localhost",port=5347, config={}):
+ PlatformAgent.__init__(self,node,passw,server,port, config)
#self.addAddress("http://"+self.getDomain()+":2099/acc") # HACK
def _setup(self):
=======================================
--- /trunk/spade/Platform.py Wed Jul 14 06:10:54 2010
+++ /trunk/spade/Platform.py Tue Jul 10 16:56:02 2012
@@ -1,8 +1,9 @@
from AMS import AmsAgentDescription
from DF import DfAgentDescription, ServiceDescription, Service
+from Agent import PlatformAgent, require_login
import xmpp
import threading
-import Agent
+#import Agent
import Envelope
import FIPAMessage
import AID
@@ -31,7 +32,7 @@
-class SpadePlatform(Agent.PlatformAgent):
+class SpadePlatform(PlatformAgent):
class RouteBehaviour(Behaviour.Behaviour):
def __init__(self):
Behaviour.Behaviour.__init__(self)
@@ -104,18 +105,15 @@
#print "Message to", to.getName(), "... Posting!"
"""
else:
- print "ACC::dying... it shouldn't happen"
+ self.myAgent.DEBUG("ACC::dying... it shouldn't
happen", 'err')
def __init__(self, node, password, server, port, config=None):
- Agent.PlatformAgent.__init__(self, node, password, server, port,
config=config, debug=[])
+ PlatformAgent.__init__(self, node, password, server, port,
config=config, debug=[])
self.mtps = {}
def _setup(self):
self.setDefaultBehaviour(self.RouteBehaviour())
- #self.addBehaviour(self.SWIBehaviour())
- #swi.SWIHandler.platform = self
- #self.wui = WUI(self)
self.wui.registerController("index",self.index)
self.wui.registerController("agents", self.agents)
self.wui.registerController("services", self.services)
@@ -162,12 +160,14 @@
platform = self.getName()
version = str(sys.version)
the_time = str(time.ctime())
- doc_path = abspath('.')
+ doc_path = abspath('.')
return "webadmin_indigo.pyra",
dict(name=platform,servername=servername, platform=platform,
version=version, time=the_time, doc_path=doc_path)
+ @require_login
def agents(self):
import sys
import time
+ so = self.session
servername = self.getDomain()
platform = self.getName()
version = str(sys.version)
@@ -192,6 +192,7 @@
self.DEBUG("AWUIs: "+str(awuis))
return "agents.pyra", dict(name=platform,servername=servername,
platform=platform, version=version, time=the_time, agents=search,
awuis=awuis)
+ @require_login
def services(self):
import sys
import time
=======================================
--- /trunk/spade/SpadeConfigParser.py Sat Jun 19 14:28:27 2010
+++ /trunk/spade/SpadeConfigParser.py Tue Jul 10 16:56:02 2012
@@ -125,6 +125,9 @@
elif name == "path":
self.content.platform["path"] = self.chars
+ elif name == "adminpasswd":
+ self.content["adminpasswd"] = self.chars
+
class ConfigParser:
"""def get(self, section, keyword):
=======================================
--- /trunk/spade/bdi.py Thu Jun 24 16:31:43 2010
+++ /trunk/spade/bdi.py Tue Jul 10 16:56:02 2012
@@ -2,10 +2,16 @@
from copy import copy
import types
-#from spade import *
import Agent
import Behaviour
+import SPARQLKB
+import XSBKB
+import Flora2KB
+import SWIKB
+import ECLiPSeKB
+
+
class PreConditionFailed (Exception): pass
class PostConditionFailed(Exception): pass
class ServiceFailed(Exception): pass
@@ -148,27 +154,48 @@
self._needDeliberate = True
-
- def addBelieve(self, sentence):
- if isinstance(sentence,types.StringType):
+ def configureKB(self, typ, sentence=None, path=None):
+ kbs = ["ECLiPSe", "Flora2", "SPARQL", "SWI", "XSB"]
+ try:
+ if not (typ in kbs): raise Exception
+ typ+="KB"
+ #module = eval("__import__("+typ+")")
+
+ if path!=None:
+ self.kb =
eval(typ+"."+typ+"("+str(sentence)+", '"+path+"')")
+ else:
+ self.kb = eval(typ+"."+typ+"("+str(sentence)+")")
+ except Exception, e:
+ print str(e)
+ self.DEBUG("Could not use " + typ + "KB. Using Fol
KB.", 'warn')
+ self.kb = FolKB()
+
+
+ def addBelieve(self, sentence, type="insert"):
+ if isinstance(sentence,types.StringType) and issubclass(FolKB,
self.kb.__class__):
self.kb.tell(expr(sentence))
- else:
- self.kb.tell(sentence)
+ elif issubclass(Flora2KB.Flora2KB,self.kb.__class__):
+ self.kb.tell(sentence,type)
+
+ else:
+ self.kb.tell(sentence)
self._needDeliberate = True
self.newBelieveCB(sentence)
- def removeBelieve(self, sentence):
- if isinstance(sentence,types.StringType):
+ def removeBelieve(self, sentence, type="delete"):
+ if isinstance(sentence,types.StringType) and issubclass(FolKB,
self.kb.__class__):
self.kb.retract(expr(sentence))
+ elif issubclass(Flora2KB.Flora2KB,self.kb.__class__):
+ self.kb.retract(sentence,type)
else:
- self.kb.retract(sentence)
+ self.kb.retract(sentence)
self._needDeliberate = True
def askBelieve(self, sentence):
- if isinstance(sentence,types.StringType):
+ if isinstance(sentence,types.StringType) and issubclass(FolKB,
self.kb.__class__):
return self.kb.ask(expr(sentence))
else:
- return self.kb.ask(sentence)
+ return self.kb.ask(sentence)
def addPlan(self, plan):
plan.addOwner(self)
=======================================
--- /trunk/spade/logic.py Wed Oct 14 10:05:24 2009
+++ /trunk/spade/logic.py Tue Jul 10 16:56:02 2012
@@ -26,8 +26,6 @@
from __future__ import generators
import re
-#import agents
-#from utils import *
def every(predicate, seq):
"""True if every element of seq satisfies predicate.
=======================================
--- /trunk/spade/spade_backend.py Sat Jun 19 14:28:27 2010
+++ /trunk/spade/spade_backend.py Tue Jul 10 16:56:02 2012
@@ -37,10 +37,12 @@
server = config["platform"]['hostname']
port = int( config[section]['port'] )
jid = section + "." + server
+ if not "adminpasswd" in config.keys(): config["adminpasswd"]=None
if section == "acc":
agent = agentClass(jid, passwd, server, port, config=config)
else:
agent = agentClass(jid, passwd, server, port)
+ agent.setAdminPasswd(config["adminpasswd"])
agent.start()
return agent
@@ -56,7 +58,7 @@
#TODO: this should be configurable
self.acc = self.runAgent(self.config, "acc",
Platform.SpadePlatform)
#self.acc._debug=True
- self.ams = self.runAgent(self.config, "ams", AMS.AMS)
+ self.ams = self.runAgent(self.config, "ams", AMS.AMS)
#self.ams.DEBUG = self.acc.DEBUG
self.df = self.runAgent(self.config, "df", DF.DF)
#self.df.DEBUG = self.acc.DEBUG
=======================================
--- /trunk/spade/wui.py Wed Jul 14 06:10:54 2010
+++ /trunk/spade/wui.py Tue Jul 10 16:56:02 2012
@@ -11,9 +11,17 @@
import time
import random
import socket
+import Cookie
+import string
from threading import Thread
+chars = string.ascii_letters + string.digits
+sessionDict = {} # dictionary mapping session id's to session objects
+
+class HTTP_REDIRECTION(Exception):
+ pass
+
class WUI(Thread):
def __init__(self, owner):
Thread.__init__(self)
@@ -30,8 +38,7 @@
self.controllers = {}
self.is_running = False
-
-
+
def run(self):
while not self.httpd:
@@ -89,9 +96,38 @@
return "501.pyra", {"template":"501.pyra", "error":""}
def error503(self):
return "503.pyra", {"page":"503.pyra"}
+
+
+class SessionElement(object):
+ """Arbitrary objects, referenced by the session id"""
+ pass
+
+def generateRandom(length):
+ """Return a random string of specified length (used for session id's)"""
+ return ''.join([random.choice(chars) for i in range(length)])
class WUIHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
-
+
+ def Session(self):
+ """Session management
+ If the client has sent a cookie named sessionId, take its value and
+ return the corresponding SessionElement objet, stored in
+ sessionDict
+ Otherwise create a new SessionElement objet and generate a random
+ 8-letters value sent back to the client as the value for a cookie
+ called sessionId"""
+ if self.cookie.has_key("sessionId"):
+ sessionId=self.cookie["sessionId"].value
+ else:
+ sessionId=generateRandom(8)
+ self.cookie["sessionId"]=sessionId
+ try:
+ sessionObject = sessionDict[sessionId]
+ except KeyError:
+ sessionObject = SessionElement()
+ sessionDict[sessionId] = sessionObject
+ return sessionObject
+
def getPage(self, req):
"""
Return the page name from a raw GET line
@@ -143,28 +179,34 @@
"""
GET petitions handler
"""
-
ret = None
request = self.raw_requestline.split()
page = self.getPage(request[1])
+
if page == "/": page = "/index"
if page.startswith("/"): page = page [1:]
-
+
try:
- vars = self.getVars("?"+self._POST_REQ)
+ req_vars = self.getVars("?"+self._POST_REQ)
except:
- vars = self.getVars(request[1])
+ req_vars = self.getVars(request[1])
s_vars=""
- for k,v in vars.items():
+ for k,v in req_vars.items():
if k:
v = str(v)
if not v.startswith('"') and not v.startswith("'") and not
v.startswith("["):
v = '"'+ v +'"'
s_vars+= str(k) + "=" + v +","
if s_vars.endswith(","): s_vars = s_vars[:-1]
+
+ #session management
+ if self.headers.has_key('cookie'):
+
self.cookie=Cookie.SimpleCookie(self.headers.getheader("cookie"))
+ else:
+ self.cookie=Cookie.SimpleCookie()
# Switch page
#if page.endswith("css"):
@@ -180,13 +222,23 @@
page = self.server.owner.spade_path + os.sep + page
else:
raise Exception
- f = open(page, "r")
- self.copyfile(f, self.wfile)
- f.close()
+
except:
self.server.owner.owner.DEBUG("Could not open file: "+
page, "err")
+
+ try:
+ self.send_response(200)
+ for morsel in self.cookie.values():
+ self.send_header('Set-Cookie',
morsel.output(header='').lstrip())
+ self.end_headers()
+ f = open(page, "r")
+ self.copyfile(f, self.wfile)
+ f.close()
+ except:
+ self.server.owner.owner.DEBUG(sys.exc_info(), "err")
else:
+ sess = self.Session()
try:
# Check wether controller exists
# Get the first section of the URL path (e.g. the "admin"
of "admin/foo/bar")
@@ -198,31 +250,52 @@
_err
= ''.join(traceback.format_exception(_exception[0], _exception[1],
_exception[2])).rstrip()
template = "404.pyra"
ret =
{"template":page, "error":str(_err),"name":self.server.owner.owner.getName()}
+ code=404
try:
if not ret:
- func = self.server.owner.controllers[str(page)]
+ func = self.server.owner.controllers[str(page)]
+ #session object
+ sess.url = page
+ func.__self__.session = sess
template, ret = eval("func"+"("+s_vars+")")
+ code=200
+ except HTTP_REDIRECTION,url:
+ self.send_response(302)
+ for morsel in self.cookie.values():
+ self.send_header('Set-Cookie',
morsel.output(header='').lstrip())
+ self.send_header('Location', url)
+ self.end_headers()
+ self.wfile.write("")
+ return
+
except Exception, e:
#No controller
_exception = sys.exc_info()
if _exception[0]:
_err
= ''.join(traceback.format_exception(_exception[0], _exception[1],
_exception[2])).rstrip()
template = "501.pyra"
- ret = {"template":page, "error":str(_err)}
+ ret =
{"template":page, "error":str(_err),"name":self.server.owner.owner.getName()}
+ code=501
try:
if
os.path.exists(self.server.owner.template_path+os.sep+template):
t =
pyratemp.Template(filename=self.server.owner.template_path+os.sep+template,
data=ret)
else:
- olddir = os.path.curdir
+ #olddir = os.path.curdir
#os.chdir(self.server.spade_path)
+ authenticated = False
+ if hasattr(sess,"user_authenticated"):
+ if sess.user_authenticated==True: authenticated=True
+ ret['authenticated'] = authenticated
t =
pyratemp.Template(filename=self.server.owner.spade_path+os.sep+"templates"+os.sep+template,
data=ret)
+ #print template, ret
#os.chdir(olddir)
except pyratemp.TemplateSyntaxError, e:
_exception = sys.exc_info()
if _exception[0]:
_err
= ''.join(traceback.format_exception(_exception[0], _exception[1],
_exception[2])).rstrip()
t =
pyratemp.Template(filename=self.server.owner.spade_path+os.sep+"templates"+os.sep+"501.pyra",
data={"template":template, "error":str(_err),"name":self.server.owner.owner.getName()})
+ code=501
except Exception, e:
#No template
_exception = sys.exc_info()
@@ -230,6 +303,7 @@
_err
= ''.join(traceback.format_exception(_exception[0], _exception[1],
_exception[2])).rstrip()
#print "###", _err, "###"
t =
pyratemp.Template(filename=self.server.owner.spade_path+os.sep+"templates"+os.sep+"503.pyra",
data={"page":template,"name":self.server.owner.owner.getName()})
+ code=503
try:
result = t()
except Exception, e:
@@ -239,13 +313,15 @@
_err
= ''.join(traceback.format_exception(_exception[0], _exception[1],
_exception[2])).rstrip()
t =
pyratemp.Template(filename=self.server.owner.spade_path+os.sep+"templates"+os.sep+"501.pyra",
data={"template":template, "error":str(_err),"name":self.server.owner.owner.getName()})
result = t()
+ code=501
r = result.encode("ascii", 'xmlcharrefreplace')
+ self.send_response(code)
+ for morsel in self.cookie.values():
+ self.send_header('Set-Cookie',
morsel.output(header='').lstrip())
+ self.end_headers()
self.wfile.write(r)
-
-
-
=======================================
--- /trunk/templates/header.pyra Thu Jul 8 05:33:24 2010
+++ /trunk/templates/header.pyra Tue Jul 10 16:56:02 2012
@@ -7,7 +7,7 @@
<div id='templatemo_logo_section'>
<div class='header section' id='header'><div class='widget Header'
id='Header1'>
<div id='header-inner'>
- <a href='
http://spadeagents.blogspot.com/' style='display: block'>
+ <a href='index' style='display: block'>
<img alt='Smart Python multi-Agent Development Environment' height='40'
id='Header1_headerimg'
src='
http://3.bp.blogspot.com/_huwkhz9gyDI/TBAefGNkLlI/AAAAAAAAAUs/JB5tJkmOcl8/S1600-R/spade_logo_alpha_bco.png'
style='display: block'/>
</a>
</div>
@@ -54,6 +54,12 @@
<!--(else)-->
<li><a href='log' title='Log'><span class='left'>Log</span></a></li>
<!--(end)-->
+
+ <!--(if authenticated==True)-->
+ <li><a href='logout' title='Logout'><span
class='left'>Logout</span></a></li>
+ <!--(else)-->
+ <li><a href='login' title='Login'><span
class='left'>Login</span></a></li>
+ <!--(end)-->
</ul>
</div>
</div>
@@ -64,7 +70,7 @@
<!--(macro footer)-->
<!--<div id='templatemo_footer'>
- Copyright © 2010 <a
href='
http://spade2.googlecode.com/'>Smart Python multi-Agent Development
Environment</a>
+ Copyright © 2006-2012 <a
href='
http://spade2.googlecode.com/'>Smart Python multi-Agent Development
Environment</a>
</div>-->
<br/>
</div></div>
=======================================
--- /trunk/templates/login.pyra Mon Jul 5 11:55:49 2010
+++ /trunk/templates/login.pyra Tue Jul 10 16:56:02 2012
@@ -1,11 +1,10 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="
http://www.w3.org/1999/xhtml"
- xmlns:py="
http://purl.org/kid/ns#">
+<html xmlns="
http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=UTF-8"
- http-equiv="content-type" py:replace="''"/>
+ http-equiv="content-type"/>
<title>Login</title>
<style type="text/css">
#loginBox
@@ -74,18 +73,18 @@
<body>
<div id="loginBox">
- <h1>Login</h1>
- <p>${message}</p>
- <form action="${previous_url}" method="POST">
+ <h1>@!name!@ Login</h1>
+ <p>@!message!@</p>
+ <form action="login" method="POST">
<table>
- <tr>
+ <!--<tr>
<td class="label">
<label for="user_name">User Name:</label>
</td>
<td class="field">
<input type="text" id="user_name"
name="user_name"/>
</td>
- </tr>
+ </tr>-->
<tr>
<td class="label">
<label for="password">Password:</label>
@@ -96,16 +95,14 @@
</tr>
<tr>
<td colspan="2" class="buttons">
- <input type="submit" name="login" value="Login"/>
+ <input type="submit" value="Login"/>
</td>
</tr>
</table>
- <input py:if="forward_url" type="hidden" name="forward_url"
- value="${forward_url}"/>
-
- <input py:for="name,value in original_parameters.items()"
- type="hidden" name="${name}" value="${value}"/>
+ <input type="hidden" name="forward_url"
+ value="@!forward_url!@"/>
+
</form>
</div>
</body>
=======================================
--- /trunk/tlslite/utils/hmac.py Wed Jun 21 11:10:23 2006
+++ /trunk/tlslite/utils/hmac.py Tue Jul 10 16:56:02 2012
@@ -29,15 +29,19 @@
digestmod: A module supporting PEP 247. Defaults to the md5 module.
"""
if digestmod is None:
- import md5
- digestmod = md5
+ #import md5
+ import hashlib
+ #digestmod = md5
+ digestmod = hashlib
if key == None: #TREVNEW - for faster copying
return #TREVNEW
self.digestmod = digestmod
- self.outer =
digestmod.new()
- self.inner =
digestmod.new()
+ #self.outer =
digestmod.new()
+ self.outer = digestmod.md5()
+ #self.inner =
digestmod.new()
+ self.inner = digestmod.md5()
self.digest_size = digestmod.digest_size
blocksize = 64
@@ -45,7 +49,8 @@
opad = "\x5C" * blocksize
if len(key) > blocksize:
- key =
digestmod.new(key).digest()
+ #key =
digestmod.new(key).digest()
+ key = digestmod.md5(key).digest()
key = key + chr(0) * (blocksize - len(key))
self.outer.update(_strxor(key, opad))
=======================================
--- /trunk/xmppd/modules/__init__.py Fri Jul 16 10:41:53 2010
+++ /trunk/xmppd/modules/__init__.py Tue Jul 10 16:56:02 2012
@@ -3,9 +3,11 @@
import os
#from psyco.classes import *
-for m in os.listdir('modules'):
- if m[:2]=='__' or m[-3:]<>'.py': continue
- exec "import "+m[:-3]
+#for m in os.listdir('modules'):
+# if m[:2]=='__' or m[-3:]<>'.py': continue
+# exec "import "+m[:-3]
+
+import
jep0078,roster,config,message,router,db_fake,muc,stream,dialback,oob,jep0077,pubsub,wq
addons = [
# System stuff
=======================================
--- /trunk/xmppd/modules/router.py Fri Jun 25 10:39:24 2010
+++ /trunk/xmppd/modules/router.py Tue Jul 10 16:56:02 2012
@@ -1385,7 +1385,6 @@
# 5. Else if the JID is of the form <user@domain> and there are no
available resources associated with
# the user, how the stanza is handled depends on the stanza type:
else:
- print "SPOOOOOTTTTTTTSPOOOOOTTTTTTT 2"
self.intra_route(stanza) ## Try to outsource this guy!
# 1. For presence stanzas of
type "subscribe", "subscribed", "unsubscribe", and "unsubscribed",