pysesame-0.24/0000755000175000017500000000000010636042477012240 5ustar nachonachopysesame-0.24/examples.py0000755000175000017500000001632510354522632014433 0ustar nachonacho# 20051228 *pike # 20050628 *pike # very simple example scripts for pySesame # this does the same as tests.py, but in a human way. import pySesame # CONFIG --------------------- # select an *empty* test database, plain rdf sail # on which the user has read and write access url = 'http://localhost:8080' path = '/sesame/' user = 'yourusername' password = 'youruserpassword' repository = 'yourrepositoryid' # toggle tests to perform tests = {} tests["anon"] = True tests["login"] = True tests["clear"] = True tests["upload"] = True tests["uploadbroken"] = True tests["uploadurl"] = True tests["extract"] = True tests["rdql"] = True tests["rdqlrdf"] = True tests["serqls"] = True tests["serqlc"] = True tests["remove"] = True # ---------------------------- #import some example data from tests_data import w3c, w3cRemoved, w3cResult, postURL from tests_data import serqlSQuery, serqlCQuery, rdqlQuery, rdqlQueryResult from tests_data import rdqlQueryRDF, serqlCQueryRDF, postcon print "Note: \nmake sure your repository (%s@%s%s) is *empty*. \nIt will be trashed during the tests."%(repository,url,path) if tests["anon"]: resp = raw_input("\nNext test: anonymous login. Continue? [Y/n] ") if (resp.lower()=='n'): exit() # test some features on anonymous acces print 24*'=' print 'Testing anonymous connection' connection = pySesame.SesameConnection(url, path, 'debug') repos = connection.repositories() print 'There are %s repositories available for the anonymous user:'%len(repos) print "\tsesame id\t:read\t:write\t:title" for repo in repos: print "\t%(id)s\t:%(readable)s\t:%(writeable)s\t:%(title)s"%repo print 'closing anonymous connection' connection.close() # test login / logoff if tests["login"]: resp = raw_input("\nNext test: login / logoff. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'Testing login connection' connection = pySesame.SesameConnection(url, path, 'debug') connection.login(user, password) print 'received sessionid %s '%connection.cookie repos = connection.repositories() print 'There are %s repositories available for user %s:'%(len(repos),user) print "\tsesame id\t:read\t:write\t:title" for repo in repos: print "\t%(id)s\t:%(readable)s\t:%(writeable)s\t:%(title)s"%repo print 'logging out' connection.logout() print 'closing sesame connection .. which is useless because I already logged out' connection.close() # more tests require logging in resp = raw_input("\nNext test: clear. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print print 'loggin in again, for the next set of tests:' connection = pySesame.SesameConnection(url, path, 'debug') connection.login(user, password) if tests["clear"]: print 24*'=' print 'Testing clearing repo %s'%repository connection.clear(repository) print "....ok" if tests["upload"]: resp = raw_input("\nNext test: upload. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'Testing uploading good rdf' connection.uploadData(repository, postcon) print 'good, clearing repo %s'%repository connection.clear(repository) if tests["uploadbroken"]: resp = raw_input("\nNext test: uploadbroken. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'Testing uploading broken rdf' try: connection.uploadData(repository,postcon[:100]) print 'bad ! .. I didn\'t get get an error ?' except: print 'good !.. I got an error :)' if tests["uploadurl"]: resp = raw_input("\nNext test: uploadurl. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'Testing uploading from url' connection.uploadURL(repository, postURL) print 'good, clearing repo %s'%repository connection.clear(repository) if tests["extract"]: resp = raw_input("\nNext test: extract. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'Testing extracting the repo' print 'uploading----------' print w3c connection.uploadData(repository, w3c) result = connection.extract(repository, 'on', 'on') print '--------------------' if result!=w3cResult: print "unexpected result ? ----------" print result print '--------------------' else: print "ok" print 'clearing repo %s'%repository connection.clear(repository) if tests["rdql"]: resp = raw_input("\nNext test: rdql. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'testing rdqlquery with table result' print 'uploading some dummy data ..' connection.uploadData(repository, postcon) print 'submitting query----------' print rdqlQuery result = connection.tableQuery(repository, 'RDQL', rdqlQuery) if result != rdqlQueryResult: print "unexpected result ? ----------" print result print '--------------------' else: print "ok" print 'clearing repo %s'%repository connection.clear(repository) if tests["rdqlrdf"]: resp = raw_input("\nNext test: rdqlrdf. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'testing rdqlquery with rdf result' print 'uploading some dummy data ..' connection.uploadData(repository, postcon) print 'submitting query----------' print rdqlQuery result = connection.tableQuery(repository, 'RDQL', rdqlQuery,'rdf', 'rdfxml') if result != rdqlQueryRDF: print "unexpected result ? ----------" print result print '--------------------' else: print "ok" print 'clearing repo %s'%repository connection.clear(repository) if tests["serqls"]: resp = raw_input("\nNext test: serqls. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'testing serql select query' print 'uploading some dummy data ..' connection.uploadData(repository, postcon) print 'submitting query----------' print serqlSQuery result = connection.tableQuery(repository, 'SeRQL', serqlSQuery) if result != rdqlQueryResult: print "unexpected result ? ----------" print result print '--------------------' else: print "ok" print 'clearing repo %s'%repository connection.clear(repository) if tests["serqlc"]: resp = raw_input("\nNext test: serqlc. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'testing serql construct query' print 'uploading some dummy data ..' connection.uploadData(repository, postcon) print 'submitting query----------' print serqlCQuery result = connection.graphQuery(repository, 'SeRQL', serqlCQuery, 'rdfxml') if result != serqlCQueryRDF: print "unexpected result ? ----------" print result print '--------------------' else: print "ok" print 'clearing repo %s'%repository connection.clear(repository) if tests["remove"]: resp = raw_input("\nNext test: remove. Continue? [Y/n] ") if (resp.lower()=='n'): exit() print 24*'=' print 'testing remove query' print 'uploading some dummy data ..' connection.uploadData(repository, w3c) print 'submitting query----------' connection.remove(repository, '', '', '"World Wide Web Consortium"') result = connection.extract(repository, 'on', 'on') if result != w3cRemoved: print "unexpected result ? ----------" print result print '--------------------' else: print "ok" print 'clearing repo %s'%repository connection.clear(repository) connection.close() pysesame-0.24/pySesame.log0000644000175000017500000000046210262714616014527 0ustar nachonacho2005-06-28 18:01:51,684 INFO SesameConnection starting up... 2005-06-28 18:01:51,685 DEBUG GET:http://localhost:7080/sesame/servlets/listRepositories 2005-06-28 18:01:51,691 DEBUG OK 2005-06-28 18:01:51,749 DEBUG POST:http://localhost:7080/sesame/servlets/logout 2005-06-28 18:01:51,754 DEBUG OK: 23 bytes pysesame-0.24/pySesame.py0000755000175000017500000002770310354521655014411 0ustar nachonacho# # pySesame is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # pySesame 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 General Public License for more details. # # A copy of the full General Public License is available at # http://www.gnu.org/copyleft/gpl.html and from the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. # # 20050606 *pike # This version was modified to work with python 2>, which has # notable differences in the httplib module. I have kept the # interface the same. # import logging from string import split from urllib2 import urlopen from urllib2 import Request from urllib import urlencode from xml.sax.handler import ContentHandler from xml.sax import make_parser query_paths = {'login':'servlets/login', 'logout':'servlets/logout', 'repositories':'servlets/listRepositories', 'query':'servlets/evaluateTableQuery', 'construct':'servlets/evaluateGraphQuery', 'extract':'servlets/extractRDF', 'uploadData':'servlets/uploadData', 'uploadURL':'servlets/uploadURL', 'clear':'servlets/clearRepository', 'remove':'servlets/removeStatements?', } class SesameConnection: "The main class for communicating with a Sesame Server." def __init__(self, url, path, logLevel="warning", logPath=""): self.hdlr = None self.setLogger(logLevel, logPath) self.url = url self.path = path self.cookie = "" self.resetQuery() self.logger.info('SesameConnection starting up...') def close(self): self.logout() self.logger.info('...SesameConnection closing down.') self.logger.removeHandler(self.hdlr) def setLogger(self, level, path): if self.hdlr: self.logger.removeHandler(self.hdlr) levelDict = {'debug':logging.DEBUG, 'info':logging.INFO, 'warning':logging.WARNING, 'error':logging.ERROR, 'critical':logging.CRITICAL} self.logger = logging.getLogger() self.hdlr = logging.FileHandler(path + 'pySesame.log') formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') self.hdlr.setFormatter(formatter) self.logger.addHandler(self.hdlr) self.logger.setLevel(levelDict[level]) def resetQuery(self): self.query = {} def doRequest(self, request, handler=None, post=None): # Sesame uses the text/plain content type for XML responses request.add_header("Accept", "text/plain") request.add_header("User-Agent", "PySesame 0.2") if self.cookie != "": cookie_string = "sesame_sid=%s" % self.cookie request.add_header("Cookie", cookie_string) if post != None: request.add_data(post) self.logger.debug(request.get_method()+":" + request.get_full_url()) try: response = urlopen(request) # debug alls ok. i'd like to print # the Status header, but urllib2 has already eaten it # and thrown its appropriate errors or followed # any redirects etc if response.headers.has_key('Content-Length'): self.logger.debug("OK: "+response.headers['Content-Length']+" bytes") else: self.logger.debug("OK") except Exception, error: self.logger.exception(error) raise SesameServerError(error) if response: if handler == "login": if not self.CookieHandler(response): raise SesameServerError("No SessionID in cookie") # returns None on succes .... elif handler: saxparser = make_parser() saxparser.setContentHandler(handler) parsed = saxparser.parse(response) response.close() return handler.results else: results = response.readlines() resultString = "" resultString = resultString.join(results) return resultString def login(self, user, password): """ Gets your cookie. Returns None if successful. Throws an error if not """ self.resetQuery() self.query['user'] = user self.query['password'] = password query = urlencode(self.query) base = self.path + query_paths['login'] request = Request(self.url+base) return self.doRequest(request, "login",query) def logout(self): """ Logs you out. Returns 'Logged out successfully' if successful. """ self.resetQuery() base = self.path + query_paths['logout'] request = Request(self.url+base) return self.doRequest(request,None,"post") def repositories(self): """ Returns a list of dictionaries about available repositories. The dictionary keys are "id", "title", "readable" and "writeable" """ self.resetQuery() base = self.path + query_paths['repositories'] query = urlencode(self.query) request = Request(self.url + base + query) handler = RepositoriesHandler() return self.doRequest(request, handler) def tableQuery(self, repository, queryLanguage, query, resultFormat="xml", serialization = "rdfxml"): """ xml format results are returned as a data structure as follows: * a dictionary with two keys, "header" and "tuples" * header is a list of string values * tuples is a list of lists. * each sub-list is like a row in the table * each sub-list is made up of dictionaries, wherein: * the key a string made from the text of the node * the value is the type ('uri', 'literal' or 'bNode') All other formats are returned as a string. """ self.resetQuery() self.query['repository'] = repository self.query['queryLanguage'] = queryLanguage self.query['resultFormat'] = resultFormat self.query['query'] = query if resultFormat == 'rdf': self.query['serialization'] = serialization handler = None elif resultFormat == 'html': handler = None else: handler = SelectQueryHandler() base = self.path + query_paths['query'] request = Request(self.url+base) query = urlencode(self.query) return self.doRequest(request, handler, query) def graphQuery(self, repository, queryLanguage, query, serialization = "rdfxml"): """ Returns a string. """ self.resetQuery() self.query['repository'] = repository self.query['queryLanguage'] = queryLanguage self.query['query'] = query self.query['serialization'] = serialization handler = None # query_path *was* 'query' .. seemed wrong .. base = self.path + query_paths['construct'] request = Request(self.url+base) query = urlencode(self.query) return self.doRequest(request, handler, query) def extract(self, repository, schema="no", data="no", explicitOnly="no", niceOutput="no", serialization="rdfxml"): """ Returns a string. """ self.resetQuery() self.query['repository'] = repository self.query['schema'] = schema self.query['data'] = data self.query['explicitOnly'] = explicitOnly self.query['niceOutput'] = niceOutput self.query['serialization'] = serialization base = self.path + query_paths['extract'] query = urlencode(self.query) request = Request(self.url + base + '?' + query) return self.doRequest(request) def uploadData(self, repository, data = "", dataFormat="rdfxml", baseURL="", format="xml", verifyData="yes"): """ Adds a string ("data") of RDF data to the repository. Returns 1 if successful. """ self.resetQuery() self.query['data'] = data self.query['repository'] = repository self.query['dataFormat'] = dataFormat self.query['baseURL'] = baseURL self.query['format'] = format base = self.path + query_paths['uploadData'] request = Request(self.url+base) query = urlencode(self.query) handler = PostHandler() return self.doRequest(request, handler, query) def uploadURL(self, repository, dataURL, dataFormat="rdfxml", baseURL="", format="xml", verifyData="yes"): """ Attempts to add whatever is at dataURL to the repository. Returns 1 if successful. """ self.resetQuery() self.query['url'] = dataURL self.query['repository'] = repository self.query['dataFormat'] = dataFormat self.query['baseURL'] = baseURL self.query['format'] = format base = self.path + query_paths['uploadURL'] request = Request(self.url+base) query = urlencode(self.query) handler = PostHandler() return self.doRequest(request, handler, query) def clear(self, repository, format="xml"): """ Clears a repository. Returns 1 if successful. """ self.resetQuery() self.query['repository'] = repository base = self.path + query_paths['clear'] request = Request(self.url+base) query = urlencode(self.query) handler = PostHandler() return self.doRequest(request, handler, query) def remove(self, repository, subject, predicate, object, resultFormat="xml"): """ Removes statements from repository. Returns 1 if successful. """ self.resetQuery() self.query['repository'] = repository base = self.path + query_paths['remove'] request = Request(self.url+base) if subject: self.query['subject'] = subject if predicate: self.query['predicate'] = predicate if object: self.query['object'] = object self.query['resultFormat'] = resultFormat query = urlencode(self.query) handler = PostHandler() return self.doRequest(request, handler, query) def CookieHandler(self, response): value = False info = response.info() cookies = info.getheaders("Set-Cookie") for cookie in cookies: if 'sesame_sid' in cookie: half = split(cookie,';')[0] value = split(half,'=')[-1] self.logger.debug('Using cookie %s' % value) self.cookie = value return value class RepositoriesHandler(ContentHandler): """Creates a list of dictionaries describing available servers.""" def __init__(self): self.inRepository = 0 self.inTitle = 0 self.repository = {} self.results = [] def startElement(self, name, attrs): if self.inRepository and name == "title": self.inTitle = 1 self.title = "" elif name == "repository": self.repository['id'] = attrs.get('id', '') self.repository['readable'] = attrs.get('readable', '') self.repository['writeable'] = attrs.get('writeable', '') self.inRepository = 1 def characters(self, ch): if self.inTitle: self.title = ch def endElement(self, name): if name == 'title': self.repository['title'] = self.title self.inTitle = 0 elif name == 'repository': self.results.append(self.repository) self.repository = {} self.inRepository = 0 class SelectQueryHandler(ContentHandler): """.""" # this doesn't handle the header yet, right? def __init__ (self): self.types = ['uri', 'literal', 'bNode','null'] self.inTuple = 0 self.inResult = 0 self.inHeader = 0 self.header = [] self.tuple = [] self.tuples = [] def startElement(self, name, attrs): if self.inTuple and name in self.types: self.inResult = 1 self.value = "" self.type = name elif name == 'tuple': self.inTuple = 1 self.inResult = 0 elif name == 'columnName': self.inHeader = 1 def characters(self, ch): if self.inResult: self.value=ch self.tuple.append({'value':self.value,'type':self.type}) if self.inHeader: self.header.append(ch) def endElement(self, name): if name == 'tuple': self.tuples.append(self.tuple) self.tuple = [] self.inTuple = 0 if name in self.types: self.inResult = 0 elif name == 'columnName': self.inHeader = 0 elif name == 'tableQueryResult': self.results = {'header':self.header, 'tuples':self.tuples} class PostHandler(ContentHandler): """Returns 1 if successful. Throws an exception if not.""" def __init__(self): self.results = 1 self.inMsg = 0 self.line = "" self.elements = ['msg', 'line', 'column'] def startElement(self, name, attrs): if name == "error": self.results = 0 if name in ['status', 'notification', 'warning', 'error']: self.tag = name if name in self.elements: self.inMsg = 1 def characters(self, ch): if self.inMsg: self.line += "%s: %s, " % (self.tag, ch) def endElement(self, name): if name == "error": raise SesameServerError(self.line) self.line = "" if name in self.elements: self.inMsg = 0 class SesameServerError(Exception): pass pysesame-0.24/tests.py0000755000175000017500000001110510354522713013746 0ustar nachonacho # py_sesame tests.py # # this is a testsuite as used by python's unittest module # it runs a dozen of tests, catches exceptions, and prints reports # # if the test fails it doesn't necessarily mean Sesame is broken, # it only means the test failed. please check out the purpose # of the test and the nature of the failure. usually, Sesame # returned a result in a slightly different format then the test # expected; e.g. rdf/xml with some extra whitespace, which is # harmless. import unittest import pySesame # CONFIG --------------------- # select an *empty* test database, plain rdf sail # on which the user has read and write access url = 'http://localhost:8080' path = '/sesame/' user = 'yourusername' password = 'youruserpassword' repository = 'yourrepositoryid' # ---------------------------- #20050628 *pike moved data out of the file #20050628 *pike added serql tests #import some example data from tests_data import w3c, w3cRemoved, w3cResult, postURL from tests_data import serqlSQuery, serqlCQuery, rdqlQuery, rdqlQueryResult from tests_data import rdqlQueryRDF, serqlCQueryRDF, postcon class AnonymousCase(unittest.TestCase): def setUp(self): self.connection = pySesame.SesameConnection(url, path, 'debug') def tearDown(self): self.connection.close() def testRepositories(self): # This assumes that the server has no repositories # accessible to anonymous. You can probably ignore it. assert self.connection.repositories() == [], \ 'There seem to be repositories available for the anonymous user.' def testLogin(self): assert self.connection.login(user, password) == None, \ 'Login error.' def testLogout(self): correct = 'Logged out successfully' self.connection.login(user, password) assert self.connection.logout() == correct, \ 'Logout error.' class TestUserCase(unittest.TestCase): def setUp(self): self.connection = pySesame.SesameConnection(url, path, 'debug') self.connection.login(user, password) def tearDown(self): self.connection.clear(repository) self.connection.logout() self.connection.close() def testRepositories(self): repos = self.connection.repositories() ok = True for repo in repos: if repo["id"]==repository: ok = ok and repo["writeable"] ok = ok and repo["readable"] assert ok, 'Repository %s is not readable or writable'%repository def testClearRepository(self): assert self.connection.clear(repository) == 1, \ 'Error in clear.' def testUploadBadData(self): self.assertRaises(pySesame.SesameServerError, self.connection.uploadData, postcon[:100]) def testUploadData(self): assert self.connection.uploadData(repository, postcon) == 1, \ 'Error in upload.' self.connection.clear(repository) def testUploadURL(self): assert self.connection.uploadURL(repository, postURL) == 1, \ 'Error in URL load.' def testExtract(self): "This is rather sensitive to the formatting of the result." self.connection.clear(repository) self.connection.uploadData(repository, w3c) assert self.connection.extract(repository, 'on', 'on') == w3cResult, \ 'Error in RDF extract.' def testRDQLtable(self): self.connection.clear(repository) self.connection.uploadData(repository, postcon) assert self.connection.tableQuery(repository, 'RDQL', rdqlQuery) == \ rdqlQueryResult, 'Error in RDQL table query.' def testRDQLrdf(self): self.connection.clear(repository) self.connection.uploadData(repository, postcon) assert self.connection.tableQuery(repository, 'RDQL', rdqlQuery, \ 'rdf', 'rdfxml') == rdqlQueryRDF, \ 'RDQL Query, RDF output does not return expected results' def testSeRQLselect(self): self.connection.clear(repository) self.connection.uploadData(repository, postcon) assert self.connection.tableQuery(repository, 'SeRQL', serqlSQuery) == \ rdqlQueryResult, 'SeRQL select query does not return expected results' def testSeRQLconstruct(self): self.connection.clear(repository) self.connection.uploadData(repository, postcon) assert self.connection.graphQuery(repository, 'SeRQL', serqlCQuery, \ 'rdfxml') == serqlCQueryRDF, \ 'SeRQL Construct query, RDF format does not return expected results' def testRemove(self): self.connection.clear(repository) self.connection.uploadData(repository, w3c) self.connection.remove(repository, '', \ '', \ '"World Wide Web Consortium"') assert self.connection.extract(repository, 'on', 'on') == \ w3cRemoved, 'Remove does not return expected results' if __name__ == "__main__": unittest.main() pysesame-0.24/tests_data.py0000644000175000017500000001574610263307171014751 0ustar nachonacho w3c = """ World Wide Web Consortium """ w3cRemoved = ''' ''' w3cResult = ''' World Wide Web Consortium ''' # Using Shelley Powers examples from _Practical_RDF_. Should be # pretty stable. postURL = "http://burningbird.net/articles/monsters1.rdf" serqlSQuery = """ select date from {Resource} rdf:type {pstcn:Movement}; pstcn:movementType {"Add"}; dc:date {date} using namespace pstcn = , dc = """ serqlCQuery = """ construct {Resource} dc:date {date} from {Resource} rdf:type {pstcn:Movement}; pstcn:movementType {"Add"}; dc:date {date} using namespace pstcn = , dc = """ rdqlQuery = """SELECT ?date WHERE (?resource, , ), (?resource, ,?value), (?resource, , ?date) AND (?value eq "Add") USING pstcn FOR , rdf for , dc for """ rdqlQueryResult = {'header':[u'date'], 'tuples': [[{'type':u'literal','value': '1998-01-01T00:00:00-05:00'}]]} rdqlQueryRDF = """
date
1998-01-01T00:00:00-05:00
""" serqlCQueryRDF = """ 1998-01-01T00:00:00-05:00 """ postcon = """ Tale of Two Monsters: Legends When I think of "monsters" I think of the creatures of legends and tales, from the books and movies, and I think of the creatures that have entertained me for years. Part 1 of four-part series on cryptozoology, legends, Nessie the Loch Ness Monster and the giant squid. 1999-08-01T00:00:00-06:00 Shelley Powers Burningbird Network Active 2003-12-01T00:00:00-06:00 legends giant squid loch ness monster Architeuthis Dux Nessie http://www.pibburns.com/cryptozo.htm http://www.nrcc.utmb.edu/ text/html XHTML 1.0 Strict CSS Validation HTML User agent stylesheet http://burningbird.net/de.css logo http://burningbird.net/mm/dynamicearth.jpg Cryptozooloy First in the Tale of Two Monsters series. A Tale of Two Monsters: Architeuthis Dux (Giant Squid) Second in the Tale of Two Monsters series. Nessie, the Loch Ness Monster Fourth in the Tale of Two Monsters series. Add New Article 1998-01-01T00:00:00-05:00 Move Moved to separate dynamicearth.com domain 1999-10-31:T00:00:00-05:00 Move Collapsed into Burningbird 2002-11-01 """