diff options
Diffstat (limited to 'metaweb-py')
| -rw-r--r-- | metaweb-py/metaweb.py | 469 |
1 files changed, 0 insertions, 469 deletions
diff --git a/metaweb-py/metaweb.py b/metaweb-py/metaweb.py deleted file mode 100644 index e75ee0b..0000000 --- a/metaweb-py/metaweb.py +++ /dev/null @@ -1,469 +0,0 @@ -#======================================================================== -# Copyright (c) 2007, Metaweb Technologies, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY METAWEB TECHNOLOGIES ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL METAWEB TECHNOLOGIES BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ======================================================================== -# -# This is the full "metaweb.py" module from the Metaweb API documentation -# -# In the documentation, each function is presented as a separate -# example. This is the whole file. -# -# If you find any errors or have suggestions for improving this module, -# send them to the Freebase developers mailing list: developers@freebase.com -# You can subscribe to the mailing list at http://lists.freebase.com/ -# - -import httplib -import urllib # URL encoding -import urllib2 # Higher-level URL content fetching -import simplejson # JSON serialization and parsing -import cookielib # Cookie handling -import os - -# -# When experimenting, use the sandbox.freebase.com service. -# Every Monday, sandbox.freebase.com is erased and it is updated -# with a fresh copy of data from www.freebase.com. This makes -# it an ideal place to experiment. -# -host = 'sandbox.freebase.com' # The Metaweb host -readservice = '/api/service/mqlread' # Path to mqlread service -loginservice = '/api/account/login' # Path to login service -writeservice = '/api/service/mqlwrite' # Path to mqlwrite service -uploadservice = '/api/service/upload' # Path to upload service -searchservice = '/api/service/search' # Path to search service - -credentials = None # default credential from login() -escape = False # default escape, set to 'html' for HTML escaping -permission = None # default permission used when creating new objects -debug = False # default debug setting - -# Install a CookieProcessor -cookiefile = os.path.join(os.environ["HOME"], ".metaweb.cookies.txt") -cookiejar = cookielib.LWPCookieJar() -if os.path.isfile(cookiefile): - cookiejar.load(cookiefile) - -urllib2.install_opener( - urllib2.build_opener( - urllib2.HTTPCookieProcessor(cookiejar))) - -# If anything goes wrong when talking to a Metaweb service, we raise MQLError. -class MQLError(Exception): - def __init__(self, value): # This is the exception constructor method - self.value = value - def __str__(self): # Convert error object to a string - return repr(self.value) - -# Submit the MQL query q and return the result as a Python object. -# If authentication credentials are supplied, use them in a cookie. -# Raises MQLError if the query was invalid. Raises urllib2.HTTPError if -# mqlread returns an HTTP status code other than 200 (which should not happen). -def read(q, credentials=credentials, escape=escape): - # Put the query in an envelope - envelope = {'query':q} - - # Add escape if needed - if escape != 'html': - envelope['escape'] = False if not escape else escape - - # Encode the result - encoded = urllib.urlencode({'query': simplejson.dumps(envelope)}) - - # Build the URL and create a Request object for it - url = 'http://%s%s' % (host, readservice) - req = urllib2.Request(url) - - # The body of the POST request is encoded URL parameters - req.add_header('Content-type', 'application/x-www-form-urlencoded') - - # Send our authentication credentials, if any, as a cookie. - # The need for mqlread authentication is a temporary restriction. - if credentials: req.add_header('Cookie', credentials) - - # Use the encoded envelope as the value of the q parameter in the body - # of the request. Specifying a body automatically makes this a POST. - req.add_data(encoded) - - # Now upen the URL and and parse its JSON content - f = urllib2.urlopen(req) # Open the URL - inner = simplejson.load(f) # Parse JSON response to an object - - # If anything was wrong with the invocation, mqlread will return an HTTP - # error, and the code above with raise urllib2.HTTPError. - # If anything was wrong with the query, we won't get an HTTP error, but - # will get an error status code in the response envelope. In this case - # we raise our own MQLError exception. - if not inner['code'].startswith('/api/status/ok'): - if debug: print q - if debug: print inner - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = inner['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - - # If there was no error, then just return the result from the envelope - return inner['result'] - -# Submit the MQL query q and return the result as a Python object -# This function behaves like read() above, but uses cursors so that -# it works even for very large result sets. See also the cursor class below. -def readall(q, credentials=credentials, escape=escape): - # This is the start of the mqlread URL. - # We just need to append the envelope to it - url = 'http://%s%s' % (host, readservice) - - # The query and most of the envelope are constant. We just need to append - # the encoded cursor value and some closing braces to this prefix string - jsonq = simplejson.dumps(q) - - # Add escape if needed - if escape != 'html': - jsonq += ',"escape":' + ('false' if not escape else escape) - - cursor = 'true' # This is the initial value of the cursor - results = [] # We accumulate results in this array - - # Loop until mqlread tells us there are no more results - while cursor: - # append the cursor and the closing braces to the envelope - envelope = urllib.urlencode({'query': '{"query":' + jsonq + ',"cursor":' + cursor + '}'}) - - # Begin an HTTP request for the URL - req = urllib2.Request(url) - - # The body of the POST request is encoded URL parameters - req.add_header('Content-type', 'application/x-www-form-urlencoded') - - # Send our authentication credentials, if any, as a cookie. - # The need for mqlread authentication is a temporary restriction. - if credentials: - req.add_header('Cookie', credentials) - - # Use the encoded envelope as the value of the q parameter in the body - # of the request. Specifying a body automatically makes this a POST. - req.add_data(envelope) - - # Read and parse the URL contents - f = urllib2.urlopen(req) # Open URL - inner = simplejson.load(f) # Parse JSON response - - # Raise a MQLError if there were errors - if not inner['code'].startswith('/api/status/ok'): - if debug: print q - if debug: print inner - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = inner['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - - # Append this batch of results to the main array of results. - results.extend(inner['result']) - - # Finally, get the new value of the cursor for the next iteration - cursor = inner['cursor'] - if cursor: # If it is not false, put it - cursor = '"' + cursor + '"' # in quotes as a JSON string - - # Now that we're done with the loop, return the results array - return results - -# Submit multiple MQL queries and return the result as a Python array. -# If authentication credentials are supplied, use them in a cookie. -# Raises MQLError if the query was invalid. Raises urllib2.HTTPError if -# mqlread returns an HTTP status code other than 200 (which should not happen). -def readmulti(queries, credentials=credentials, escape=escape): - encoded = "" - for i in range(0, len(queries)): - # Put the query in an envelope - envelope = {'query':queries[i]} - # Add escape if needed - if escape != 'html': - envelope['escape'] = False if not escape else escape - if i > 0: - encoded += "," - encoded += '"q%d":%s' % (i, simplejson.dumps(envelope)) - - # URL encode the outer envelope - encoded = urllib.urlencode({'queries': "{" + encoded + "}"}) - - # Build the URL and create a Request object for it - url = 'http://%s%s' % (host, readservice) - req = urllib2.Request(url) - - # The body of the POST request is encoded URL parameters - req.add_header('Content-type', 'application/x-www-form-urlencoded') - - # Send our authentication credentials, if any, as a cookie. - # The need for mqlread authentication is a temporary restriction. - if credentials: req.add_header('Cookie', credentials) - - # Use the encoded envelope as the value of the q parameter in the body - # of the request. Specifying a body automatically makes this a POST. - req.add_data(encoded) - - # Now upen the URL and and parse its JSON content - f = urllib2.urlopen(req) # Open the URL - inner = simplejson.load(f) # Parse JSON response to an object - - # If anything was wrong with the invocation, mqlread will return an HTTP - # error, and the code above with raise urllib2.HTTPError. - # If anything was wrong with the query, we won't get an HTTP error, but - # will get an error status code in the response envelope. In this case - # we raise our own MQLError exception. - if not inner['code'].startswith('/api/status/ok'): - if debug: print queries - if debug: print inner - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = inner['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - - # extract the results - results = [] - for i in range(0, len(queries)): - result = inner["q%d" % i] - if not result['code'].startswith('/api/status/ok'): - if debug: print queries[i] - if debug: print result - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = result['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - results.append(result['result']) - - # If there was no error, then just return the result from the envelope - return results - -# Submit the specified username and password to the Metaweb login service. -# Return opaque authentication credentials on success. -# Raise MQLError on failure. -def login(username, password): - # Establish a connection to the server and make a request. - # Note that we use the low-level httplib library instead of urllib2. - # This allows us to manage cookies explicitly. - conn = httplib.HTTPConnection(host) - conn.request('POST', # POST the request - loginservice, # The URL path /api/account/login - # The body of the request: encoded username/password - urllib.urlencode({'username':username, 'password':password}), - # This header specifies how the body of the post is encoded. - {'Content-type': 'application/x-www-form-urlencoded'}) - - # Get the response from the server - response = conn.getresponse() - - if response.status == 200: # We get HTTP 200 OK even if login fails - # Parse response body and raise a MQLError if login failed - body = simplejson.loads(response.read()) - if not body['code'].startswith('/api/status/ok'): - if debug: print inner - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = body['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - - # Otherwise return cookies to serve as authentication credentials. - # The set-cookie header holds one or more cookie specifications, - # separated by commas. Each specification is a name, an equal - # sign, a value, and one or more trailing clauses that consist - # of a semicolon and some metadata. We don't care about the - # metadata. We just want to return a comma-separated list of - # name=value pairs. - cookies = response.getheader('set-cookie').split(',') - return ';'.join([c[0:c.index(';')] for c in cookies]) - else: # This should never happen - raise MQLError('HTTP Error: %d %s' % (response.status,response.reason)) - - -# Submit the MQL write q and return the result as a Python object. -# Authentication credentials are required, obtained from login() -# Raises MQLError if the query was invalid. Raises urllib2.HTTPError if -# mqlwrite returns an HTTP status code other than 200 -def write(query, credentials=credentials, escape=escape, permission=permission): - # We're requesting this URL - req = urllib2.Request('http://%s%s' % (host, writeservice)) - # Send our authentication credentials as a cookie - if credentials: - req.add_header('Cookie', credentials) - # This custom header is required and guards against XSS attacks - req.add_header('X-Metaweb-Request', 'True') - # The body of the POST request is encoded URL parameters - req.add_header('Content-type', 'application/x-www-form-urlencoded') - # Wrap the query object in a query envelope - envelope = {'qname': {'query': query}} - # Add escape if needed - if escape != 'html': - envelope['qname']['escape'] = (False if not escape else escape) - # Add permissions if needed - if permission: - envelope['qname']['use_permission_of'] = permission - # JSON encode the envelope - encoded = simplejson.dumps(envelope) - # Use the encoded envelope as the value of the q parameter in the body - # of the request. Specifying a body automatically makes this a POST. - req.add_data(urllib.urlencode({'queries':encoded})) - - # Now do the POST - f = urllib2.urlopen(req) - response = simplejson.load(f) # Parse HTTP response as JSON - inner = response['qname'] # Open outer envelope; get inner envelope - - # If anything was wrong with the invocation, mqlwrite will return an HTTP - # error, and the code above with raise urllib2.HTTPError. - # If anything was wrong with the query, we will get an error status code - # in the response envelope. - # we raise our own MQLError exception. - if not inner['code'].startswith('/api/status/ok'): - if debug: print query - if debug: print inner - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = inner['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - - # save cookie - cookiejar.save(cookiefile) - - # If there was no error, then just return the result from the envelope - return inner['result'] - -# Upload the specified content (and give it the specified type). -# Return the guid of the /type/content object that represents it. -# The returned guid can be used to retrieve the content with /api/trans/raw. -def upload(content, type, credentials=credentials): - # This is the URL we POST content to - url = 'http://%s%s'%(host,uploadservice) - # Build the HTTP request - req = urllib2.Request(url, content) # URL and content to POST - req.add_header('Content-Type', type) # Content type header - if credentials: - req.add_header('Cookie', credentials) # Authentication header - req.add_header('X-Metaweb-Request', 'True') # Guard against XSS attacks - f = urllib2.urlopen(req) # POST the request - response = simplejson.load(f) # Parse the response - if not response['code'].startswith('/api/status/ok'): - if debug: print inner - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = response['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - return response['result']['id'] # Extract and return content id - -# Search for topics -def search(query, type=None, start=0, limit=0): - args = {"query": query} - if type: - args["type"] = type - if start > 0: - args["start"] = start - if limit > 0: - args["limit"] = limit - url = 'http://%s%s?%s'%(host, searchservice, urllib.urlencode(args)) - f = urllib2.urlopen(url) - response = simplejson.load(f) # Parse the response - if not response['code'].startswith('/api/status/ok'): - if debug: print query - if debug: print inner - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = response['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - return response['result'] - -# Cursor for iterating over large data sets -# For example: -# query = {"name": None, "type":"/type/media_type"} -# for row in metaweb.cursor([query]): -# print row -class cursor: - def __init__(self, query, credentials=credentials, escape=escape): - self.query = query - self.credentials = credentials - self.index = 0 - self.results = [] - self.cursor = 'true' - self.url = 'http://%s%s' % (host, readservice) - self.jsonq = simplejson.dumps(self.query) - if escape != 'html': - self.jsonq += ',"escape":' + ('false' if not escape else escape) - - def __iter__(self): - return self - - def next(self): - # return the next value - if self.index < len(self.results): - result = self.results[self.index] - self.index = self.index + 1 - return result - - # check if there is more - if not self.cursor: - raise StopIteration - - # append the cursor and the closing braces to the envelope - envelope = urllib.urlencode({'query': '{"query":' + self.jsonq + ',"cursor":' + self.cursor + '}'}) - - # Begin an HTTP request for the URL - req = urllib2.Request(self.url) - - # The body of the POST request is encoded URL parameters - req.add_header('Content-type', 'application/x-www-form-urlencoded') - - # Send our authentication credentials, if any, as a cookie. - # The need for mqlread authentication is a temporary restriction. - if self.credentials: req.add_header('Cookie', self.credentials) - - # Use the encoded envelope as the value of the q parameter in the body - # of the request. Specifying a body automatically makes this a POST. - req.add_data(envelope) - - # Read and parse the URL contents - f = urllib2.urlopen(req) # Open URL - inner = simplejson.load(f) # Parse JSON response - - # Raise a MQLError if there were errors - if not inner['code'].startswith('/api/status/ok'): - if debug: print self.query - if debug: print inner - if debug: print f.info()['X-Metaweb-Cost'] - if debug: print f.info()['X-Metaweb-TID'] - error = inner['messages'][0] - raise MQLError('%s: %s' % (error['code'], error['message'])) - - # Remember the next cursor - self.cursor = inner['cursor'] - if self.cursor: # If it is not false, put it - self.cursor = '"' + self.cursor + '"' # in quotes as a JSON string - - # Append this batch of results to the main array of results. - self.results = inner['result'] - if len(self.results) == 0: - raise StopIteration - - # Return the first result - self.index = 1 - return self.results[0] |
