"""sparqltoy.py -- a toy SPARQL parser, just for testing converts (a little bit of) SPARQL to N3QL http://www.w3.org/DesignIssues/N3QL v 1.66 2004/07/03 13:33:38 """ class Usage(Exception): """ python sparqltoy.py --test or python sparqltoy.py portnum """ def __str__(self): return self.__doc__ __version__ = "$Id: sparqltoy.py,v 1.6 2005/05/05 22:08:07 connolly Exp $" from swap import llyn, notation3 from swap.query import applyQueries from swap.webAccess import load from swap import toXML def selectParts(s): """break down SPARQL select into variables, ns bindings, and rule head >>> selectParts('PREFIX dc: SELECT ?book ?title WHERE { ?book dc:title ?title }') (['?book', '?title'], {'dc': 'http://purl.org/dc/elements/1.1/'}, '{ ?book dc:title ?title }') """ ns = {} while 1: s = s.strip() k, s = s.split(None, 1) k = k.lower() if k == 'prefix': pfx, s = s.split(None, 1) utrm, s = s.split(None, 1) ns[pfx[:-1]] = utrm[1:-1] # remove <> around and : from dc: elif k == 'select': vars = [] while 1: s = s.strip() if s[0] == '{': return (vars, ns, s) else: k, s = s.split(None, 1) if k.lower() == 'where': continue vars.append(k) def constructParts(s): """break down SPARQL construct ns bindings, antecedent, and conclusion >>> constructParts('PREFIX dc: CONSTRUCT { ?book ?title } WHERE { ?book dc:title ?title }') ({'dc': 'http://purl.org/dc/elements/1.1/'}, '{ ?book dc:title ?title }', '{ ?book ?title }') """ ns = {} while 1: #@@ factor out prefix stuff s = s.strip() k, s = s.split(None, 1) k = k.lower() if k == 'prefix': pfx, s = s.split(None, 1) utrm, s = s.split(None, 1) ns[pfx[:-1]] = utrm[1:-1] # remove <> around and : from dc: elif k == 'construct': s = s.strip() if s[0] == '{': conc, s = s.split("}", 1) conc += "}" s = s.strip() k, s = s.split(None, 1) if k.lower() == 'where': return ns, s, conc else: raise ValueError, "expecting 'where'; found " + s else: raise ValueError, "expecting '{'; found " + s else: raise ValueError, "expecting 'construct'; found " + s def mkSelectFormula(vars, ns, ant, kb=None): """make a llyn Formula for an N3QL query vars - a list of variable names ("?foo", "?bar") ns - a dictionary of namespace bindings {"dc": "http://..."} ant - an antecedent "{ ?book dc:title ?title }" """ if kb is None: kb = llyn.RDFStore() n3p = notation3.SinkParser(kb, baseURI="file:/") pfxDecls = '' for p, u in ns.items(): pfxDecls += ("@prefix %s: <%s>.\n" % (p, u)) r = pfxDecls + "<> " + ant + ("\n ; { (%s) a <#Answer> }." % (" ".join(vars))) return n3p.loadBuf(r).close() def mkConstructFormula(ns, ant, conc, kb=None): """make a llyn Formula for an N3QL query ns - a dictionary of namespace bindings {"dc": "http://..."} ant - an antecedent "{ ?book dc:title ?title }" conc - a conclusion """ if kb is None: kb = llyn.RDFStore() n3p = notation3.SinkParser(kb, baseURI="file:/") pfxDecls = '' for p, u in ns.items(): pfxDecls += ("@prefix %s: <%s>.\n" % (p, u)) r = pfxDecls + "[] " + ant + \ "\n ; " + conc + "." return n3p.loadBuf(r).close() def queryTriples(s): """@@hmm... >>> queryTriples('PREFIX dc: SELECT ?book ?title WHERE { ?book dc:title ?title }').size() 2 """ vars, ns, ant = selectParts(s) f = mkSelectFormula(vars, ns, ant) #print f.universals(), "@@vars" #for rule in mkQueryFormula(vars, ns, ant): # should be just one # for st in rule.subject(): # print st.subject(), "@@subj" # print st.predicate(), "@@pred" # print st.object(), "@@obj" return f ###### import BaseHTTPServer import cgi # for URL-encoded query parsing RDF_MediaType = "application/rdf+xml" XML_MediaType = 'text/xml; charset="utf-8"' class SparqlServer(BaseHTTPServer.HTTPServer): """export toy SPARQL service interface, for generating HTTP traces """ def __init__(self, addr, handlerClass): BaseHTTPServer.HTTPServer.__init__(self, addr, handlerClass) self._kb = llyn.RDFStore() class SparqleHandler(BaseHTTPServer.BaseHTTPRequestHandler): QPath = '/sq?' def do_GET(self): s = self.server if self.path[:len(self.QPath)] == self.QPath: rest = self.path[len(self.QPath):] self.doQ() else: self.notFound() def doQ(self): s = self.server kb = s._kb path, qs = self.path.split('?') form = cgi.parse_qs(qs) try: sparql = form['query'][0] except KeyError: self.send_response(400) self.send_header("Content-type", "text/plain") self.wfile.write("no query param") return print "@@got query:", sparql dataSet = kb.newFormula() try: ds = form['default-graph-uri'] except: pass else: for d in ds: load(kb, d, openFormula = dataSet) print "@@loaded", d, "; size=", dataSet.size() ns, ant, conc = constructParts(sparql) #@@ assume construct qf = mkConstructFormula(ns, ant, conc, kb) print "@@query:", qf.n3String() results = kb.newFormula() applyQueries(dataSet, qf, results) results.canonicalize() self.send_response(200, "result graph follows") self.send_header("Content-type", RDF_MediaType) self.end_headers() sink = toXML.ToRDF(self.wfile, "bogus:@@") kb.dumpNested(results, sink) def notFound(self): s = self.server self.send_response(404) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(""" SparqlHandler: 404: Not Found

Not Found

try: @@something else

cf 10.4.5 404 Not Found from the HTTP specification.

""") def main(argv): try: port = argv[1] port = int(port) except: raise Usage hostPort = ('127.0.0.1', int(port)) httpd = SparqlServer(hostPort, SparqleHandler) print "sparqltoy %s\nserving on port %s ..." % (__version__, port) httpd.serve_forever() ########## def _test(): import doctest doctest.testmod() if __name__ == '__main__': import sys if '--test' in sys.argv: _test() else: main(sys.argv) # $Log: sparqltoy.py,v $ # Revision 1.6 2005/05/05 22:08:07 connolly # - implemented construct, with RDF/XML output # - implemented default-graph-uri param (mouthful!) # - finished constructParts; got the order right # - added some else/exceptions to construct parsing code # - startup msg includes version # # Revision 1.5 2005/05/05 21:21:45 connolly # - starting HTTP interface # - mkQueryFormula takes a kb rather than making one # - parse a bit of CONSTRUCT as well as SELECT # - start using N3QL rather than dbview # - Usage exception doc # - import swap.llyn rather than just llyn # # Revision 1.4 2005/05/04 23:01:00 connolly # made a sparql query into an N3 formula # # Revision 1.3 2005/05/03 22:16:48 connolly # handle prefixes differently # # Revision 1.2 2005/05/03 22:10:34 connolly # knock colon off prefix # # Revision 1.1 2005/05/03 22:08:40 connolly # one test working #