Tupelo includes a Python client for the Tupelo Server Protocol. While it is fully functional, it has several limitations:
- It is not known to work over HTTPs.
- It only supports basic authentication
- Blobs, both on read and write, are held in memory prior to each operation, limiting the size of blobs that can be read and written
Getting the code
The Python client is available as part of the Tupelo 2.4 release. The Tupelo client is in a directory called tupelo-python.
Dependencies
The client requires RDFLib, which it uses to represent RDF metadata.
Usage
To create a Tupelo client object, instantiate tupelo.HttpTupeloClient
. Configure the following attributes:
hostport
- the host and port of the Tupelo Server. Defaults tolocalhost:8080
.path
- the path to the Tupelo Server endpoint. Defaults to/tupelo/tupelo
.auth
- the username and password to authenticate with, separated by a colon. Defaults touser:password
.
Once the client is configured, operations can be performed. There is no need to "close" the client when you're finished with it.
Operations
To perform an operation immediately, call the corresponding method. There is no way to "batch" or queue operations.
Data (blobs)
Blobs are simply represented as strings, and the client provides read/write/delete operations on them.
Put
To write a blob, call put
with the following parameters:
uri
- the URI of the blob you want to writedata
- the octet stream (i.e., bytes) to write
For example:
myclient.put('tag:me@foo.org,2008:blah','This is some data')
Get
To read a blob, call get
with the following parameters:
uri
- the URI of the blob you want to read
It will return the data as a string.
For example:
assert myclient.get('tag:me@foo.org,2008:blah') == 'This is some data'
If no such blob exists, tupelo.NotFoundError
will be raised.
Delete
To delete a blob, call delete
with the following parameters:
uri
- the URI of the blob you want to delete
Metadata (RDF)
Metadata is represented as RDF statements, which are represented using constructs from rdflib. Read/write/delete are provided, along with query operations.
MPut
To write metadata, call mput
with the following parameters:
graph
- an rdflib.Graph containing RDF statements
For example:
rdf = """<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:a='http://foo.fake/bar#' xmlns:t='tag:tupeloproject.org,2004:/test/'> <a:Thing rdf:about='http://foo.fake/bar#foo'> <a:yez rdf:datatype="urn:cow">zip</a:yez> <a:link> <a:Thing rdf:about='http://foo.fake/bar#baz'> <a:snooz>Hello, world</a:snooz> </a:Thing> </a:link> </a:Thing> <t:Person rdf:about='tag:tupeloproject.org,2004:/test/joe'> <t:hasParent rdf:resource='tag:tupeloproject.org,2004:/test/carolyn'/> </t:Person> <t:Person rdf:about='tag:tupeloproject.org,2004:/test/david'> <t:hasParent rdf:resource='tag:tupeloproject.org,2004:/test/carolyn'/> </t:Person> <t:Person rdf:about='tag:tupeloproject.org,2004:/test/genevieve'> <t:hasParent rdf:resource='tag:tupeloproject.org,2004:/test/carolyn'/> </t:Person> <t:Person rdf:about='tag:tupeloproject.org,2004:/test/carolyn'> <t:hasParent rdf:resource='tag:tupeloproject.org,2004:/test/zoe'/> </t:Person> </rdf:RDF> """ toPut = ConjunctiveGraph() toPut.parse(StringInputSource(rdf)) myclient.mput(toPut)
MGet
To get metadata, call mget
with the following parameters:
uri
- the URI of the RDF subject to get metadata aboutdepth
(optional, default=1) - how many "hops" to fetch (warning: high values of depth can result in poor performance)
It will return the metadata as an rdflib.ConjunctiveGraph.
For example:
fooGraph = myclient.mget('http://foo.fake/bar#foo',2)
MDelete
To delete metadata, call mput
with the following parameters:
graph
- an rdflib.Graph containing the RDF statements to delete
Deleting a statement that is not present on the server is OK and will not cause a failure.
Match
To match a triple pattern, call match
with three parameters:
subject
- the subject URIRefpredicate
- the predicate URIRefobject
- the object URIRef or literal
Each of these parameters may be None, which functions as a wildcard, or an rdflib.URIRef, which will match the URI reference exactly. In addition theobject
parameter may be a string, which will match a literal exactly.
The return value is an rdflib.Graph containing the matching triples.
For example to find all triples asserting a dc:title
of "A Tale of Two Cities":
someGraph = myclient.match(None, URIRef('http://purl.org/dc/elements/1.1/title'), 'A Tale of Two Cities')
If no triples match, an empty graph will be returned.
MQuery SELECT
To issue a SPARQL SELECT query, pass the query to mquery_select
. The result will be a list of dictionaries, each of which contains an entry for each variable binding in the solution.
For example:
for result in myClient.mquery_select("""PREFIX tt: <tag:tupeloproject.org,2004:/test/> SELECT ?gc ?gp WHERE { ?gc tt:hasParent ?p . ?p tt:hasParent ?gp . }"""): print "%s is the grandchild of %s" % (result['gc'], result['gp'])
MQuery CONSTRUCT
To issue a SPARQL CONSTRUCT query, pass the query to mquery_construct
. The result will be an rdflib.Graph containing the constructed statements.
For example:
myGpGraph = myClient.mquery_construct("""PREFIX tt: <tag:tupeloproject.org,2004:/test/> CONSTRUCT { ?gc tt:hasGrandparent ?gp } WHERE { ?gc tt:hasParent ?p . ?p tt:hasParent ? gp . }""")