From 32a3d4322fa881061ada3f8edab5832221e33958 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 20 Sep 2011 14:26:30 -0400 Subject: [PATCH] Add n-triples tests and update triples function. - triples() now returns an iterator instead of a list - a bit of cleanup - added docs for triples --- docs/index.rst | 1 + lib/pyld/jsonld.py | 68 ++++++++++++++++++++++----------------- tests/TestRunner.py | 77 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 99 insertions(+), 47 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 59711bf1..cd0556cb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,6 +20,7 @@ API Reference .. autofunction:: expand .. autofunction:: frame .. autofunction:: normalize +.. autofunction:: triples Indices and tables ------------------ diff --git a/lib/pyld/jsonld.py b/lib/pyld/jsonld.py index eb409f03..33418f4f 100644 --- a/lib/pyld/jsonld.py +++ b/lib/pyld/jsonld.py @@ -15,7 +15,7 @@ __copyright__ = "Copyright (c) 2011 Digital Bazaar, Inc." __license__ = "New BSD licence" -__all__ = ["compact", "expand", "frame", "normalize"] +__all__ = ["compact", "expand", "frame", "normalize", "triples"] import copy @@ -1738,33 +1738,6 @@ def _compareSerializations(s1, s2): rval = _compare(s1, s2[0:len(s1)]) return rval - -def triples(input, callback=None): - normalized = normalize(input) - rval = None - - # normalize input - if (callback == None): - rval = [] - def callback(s,p,o): - rval.append({'s':s, 'p':p, 'o':o }) - return True - - quit = False - for e in normalized: - s = e['@subject']['@iri'] - for p, obj in e.iteritems(): - if p == '@subject': continue - if not isinstance(obj, list): - obj = [obj] - for o2 in obj: - quit = callback(s, p, o2)==False - if quit: break - if quit: break - if quit: break - - return rval - def normalize(input): """ Normalizes a JSON-LD object. @@ -1954,3 +1927,42 @@ def frame(input, frame, options=None): :return: the framed output. """ return Processor().frame(input, frame, options) + +def _defaultTriplesCallback(s, p, o): + return {'s':s, 'p':p, 'o':o} + +def triples(input, callback=_defaultTriplesCallback): + """ + Generates triples given a JSON-LD input. Each triple that is generated + results in a call to the given callback. The callback takes 3 parameters: + subject, property, and object. If the callback returns False then this + method will stop generating triples and return. If the callback is null, + then triple objects containing "s", "p", "o" properties will be generated. + + The object or "o" property will be a JSON-LD formatted object. + + :param input: the JSON-LD input. + :param callback: the triple callback. + :param options: framing options to use. + + :return: an iterator of triples. + """ + normalized = normalize(input) + + quit = False + for e in normalized: + s = e['@subject']['@iri'] + for p, obj in e.iteritems(): + if p == '@subject': continue + if not isinstance(obj, list): + obj = [obj] + for o2 in obj: + triple = callback(s, p, o2) + quit = (triple == False) + if quit: + break + else: + yield triple + if quit: break + if quit: break + diff --git a/tests/TestRunner.py b/tests/TestRunner.py index 48d4e4d8..927bd1b3 100644 --- a/tests/TestRunner.py +++ b/tests/TestRunner.py @@ -13,6 +13,20 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'lib')) from pyld import jsonld +## +# jsonld.triples callback to create ntriples lines +def _ntriple(s, p, o): + if isinstance(o, basestring): + # simple literal + return "<%s> <%s> \"%s\" ." % (s, p, o) + elif "@iri" in o: + # object is an IRI + return "<%s> <%s> <%s> ." % (s, p, o["@iri"]) + else: + # object is a literal + return "<%s> <%s> \"%s\"^^<%s> ." % \ + (s, p, o["@literal"], o["@datatype"]) + ## # TestRunner unit testing class. # Loads test files and runs groups of tests. @@ -106,41 +120,66 @@ def main(self): count += 1 # open the input and expected result json files - inputFd = open(join(self.testdir, test['input'])) - expectFd = open(join(self.testdir, test['expect'])) - inputJson = json.load(inputFd) - expectJson = json.load(expectFd) - - resultJson = None + inputFile = open(join(self.testdir, test['input'])) + expectFile = open(join(self.testdir, test['expect'])) + inputJson = json.load(inputFile) + expectType = os.path.splitext(test['expect'])[1][1:] + if expectType == 'json': + expect = json.load(expectFile) + elif expectType == 'nt': + # read, strip non-data lines, stripe front/back whitespace, and sort + # FIXME: only handling strict nt format here + expectLines = [] + for line in expectFile.readlines(): + line = line.strip() + if len(line) == 0 or line[0] == '#': + continue + expectLines.append(line) + expect = '\n'.join(sorted(expectLines)) + + result = None testType = test['type'] if testType == 'normalize': - resultJson = jsonld.normalize(inputJson) + result = jsonld.normalize(inputJson) elif testType == 'expand': - resultJson = jsonld.expand(inputJson) + result = jsonld.expand(inputJson) elif testType == 'compact': - contextFd = open(join(self.testdir, test['context'])) - contextJson = json.load(contextFd) - resultJson = jsonld.compact(contextJson, inputJson) + contextFile = open(join(self.testdir, test['context'])) + contextJson = json.load(contextFile) + result = jsonld.compact(contextJson, inputJson) elif testType == 'frame': - frameFd = open(join(self.testdir, test['frame'])) - frameJson = json.load(frameFd) - resultJson = jsonld.frame(inputJson, frameJson) + frameFile = open(join(self.testdir, test['frame'])) + frameJson = json.load(frameFile) + result = jsonld.frame(inputJson, frameJson) + elif testType == 'triples': + result = '\n'.join( + sorted(jsonld.triples(inputJson, callback=_ntriple))) else: print "Unknown test type." # check the expected value against the test result - if expectJson == resultJson: + if expect == result: passed += 1 print 'PASS' if self.options.verbose: - print 'Expect:', json.dumps(expectJson, indent=4) - print 'Result:', json.dumps(resultJson, indent=4) + print 'Expect:', json.dumps(expect, indent=4) + print 'Result:', + if expectType == 'json': + print json.dumps(result, indent=4) + else: + print + print result else: failed += 1 print 'FAIL' - print 'Expect:', json.dumps(expectJson, indent=4) - print 'Result:', json.dumps(resultJson, indent=4) + print 'Expect:', json.dumps(expect, indent=4) + print 'Result:', + if expectType == 'json': + print json.dumps(result, indent=4) + else: + print + print result print "Tests run: %d, Tests passed: %d, Tests Failed: %d" % (run, passed, failed)