summaryrefslogtreecommitdiff
path: root/freebase/api
diff options
context:
space:
mode:
Diffstat (limited to 'freebase/api')
-rwxr-xr-xfreebase/api/__init__.py4
-rwxr-xr-xfreebase/api/httplib2cookie.py149
-rwxr-xr-xfreebase/api/mqlkey.py129
-rw-r--r--freebase/api/session.py524
4 files changed, 0 insertions, 806 deletions
diff --git a/freebase/api/__init__.py b/freebase/api/__init__.py
deleted file mode 100755
index c3adebc..0000000
--- a/freebase/api/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-
-from session import HTTPMetawebSession, MetawebError, attrdict
-
-from mqlkey import quotekey, unquotekey
diff --git a/freebase/api/httplib2cookie.py b/freebase/api/httplib2cookie.py
deleted file mode 100755
index fee0ec0..0000000
--- a/freebase/api/httplib2cookie.py
+++ /dev/null
@@ -1,149 +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 AND CONTRIBUTORS
-# ``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 OR CONTRIBUTORS 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.
-# ========================================================================
-
-#
-#
-# httplib2cookie.py allows you to use python's standard
-# CookieJar class with httplib2.
-#
-#
-
-import re
-import httplib2
-from httplib2 import Http
-
-import mimetools
-import urllib
-import urllib2
-import cookielib
-
-class DummyRequest(object):
- """Simulated urllib2.Request object for httplib2
-
- implements only what's necessary for cookielib.CookieJar to work
- """
- def __init__(self, url, headers=None):
- self.url = url
- self.headers = headers
- self.origin_req_host = cookielib.request_host(self)
- self.type, r = urllib.splittype(url)
- self.host, r = urllib.splithost(r)
- if self.host:
- self.host = urllib.unquote(self.host)
-
- def get_full_url(self):
- return self.url
-
- def get_origin_req_host(self):
- # TODO to match urllib2 this should be different for redirects
- return self.origin_req_host
-
- def get_type(self):
- return self.type
-
- def get_host(self):
- return self.host
-
- def get_header(self, key, default=None):
- return self.headers.get(key.lower(), default)
-
- def has_header(self, key):
- return key in self.headers
-
- def add_unredirected_header(self, key, val):
- # TODO this header should not be sent on redirect
- self.headers[key.lower()] = val
-
- def is_unverifiable(self):
- # TODO to match urllib2, this should be set to True when the
- # request is the result of a redirect
- return False
-
-
-class DummyResponse(object):
- """Simulated urllib2.Request object for httplib2
-
- implements only what's necessary for cookielib.CookieJar to work
- """
- def __init__(self, response):
- self.response = response
-
- def info(self):
- return DummyMessage(self.response)
-
-
-class DummyMessage(object):
- """Simulated mimetools.Message object for httplib2
-
- implements only what's necessary for cookielib.CookieJar to work
- """
- def __init__(self, response):
- self.response = response
-
- def getheaders(self, k):
- k = k.lower()
- v = self.response.get(k.lower(), None)
- if k not in self.response:
- return []
- #return self.response[k].split(re.compile(',\\s*'))
-
- # httplib2 joins multiple values for the same header
- # using ','. but the netscape cookie format uses ','
- # as part of the expires= date format. so we have
- # to split carefully here - header.split(',') won't do it.
- HEADERVAL= re.compile(r'\s*(([^,]|(,\s*\d))+)')
- return [h[0] for h in HEADERVAL.findall(self.response[k])]
-
-class CookiefulHttp(Http):
- """Subclass of httplib2.Http that keeps cookie state
-
- constructor takes an optional cookiejar=cookielib.CookieJar
-
- currently this does not handle redirects completely correctly:
- if the server redirects to a different host the original
- cookies will still be sent to that host.
- """
- def __init__(self, cookiejar=None, **kws):
- # note that httplib2.Http is not a new-style-class
- Http.__init__(self, **kws)
- if cookiejar is None:
- cookiejar = cookielib.CookieJar()
- self.cookiejar = cookiejar
-
- def request(self, uri, **kws):
- headers = kws.pop('headers', None)
- req = DummyRequest(uri, headers)
- self.cookiejar.add_cookie_header(req)
- headers = req.headers
-
- (r, body) = Http.request(self, uri, headers=headers, **kws)
-
- resp = DummyResponse(r)
- self.cookiejar.extract_cookies(resp, req)
-
- return (r, body)
diff --git a/freebase/api/mqlkey.py b/freebase/api/mqlkey.py
deleted file mode 100755
index 98b7d83..0000000
--- a/freebase/api/mqlkey.py
+++ /dev/null
@@ -1,129 +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 AND CONTRIBUTORS
-# ``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 OR CONTRIBUTORS 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.
-# ========================================================================
-
-import string
-import re
-
-def quotekey(ustr):
- """
- quote a unicode string to turn it into a valid namespace key
-
- """
- valid_always = string.ascii_letters + string.digits
- valid_interior_only = valid_always + '_-'
-
- if isinstance(ustr, str):
- s = unicode(ustr,'utf-8')
- elif isinstance(ustr, unicode):
- s = ustr
- else:
- raise ValueError, 'quotekey() expects utf-8 string or unicode'
-
- output = []
- if s[0] in valid_always:
- output.append(s[0])
- else:
- output.append('$%04X' % ord(s[0]))
-
- for c in s[1:-1]:
- if c in valid_interior_only:
- output.append(c)
- else:
- output.append('$%04X' % ord(c))
-
- if len(s) > 1:
- if s[-1] in valid_always:
- output.append(s[-1])
- else:
- output.append('$%04X' % ord(s[-1]))
-
- return str(''.join(output))
-
-
-def unquotekey(key, encoding=None):
- """
- unquote a namespace key and turn it into a unicode string
- """
-
- valid_always = string.ascii_letters + string.digits
-
- output = []
- i = 0
- while i < len(key):
- if key[i] in valid_always:
- output.append(key[i])
- i += 1
- elif key[i] in '_-' and i != 0 and i != len(key):
- output.append(key[i])
- i += 1
- elif key[i] == '$' and i+4 < len(key):
- # may raise ValueError if there are invalid characters
- output.append(unichr(int(key[i+1:i+5],16)))
- i += 5
- else:
- raise ValueError, "unquote key saw invalid character '%s' at position %d" % (key[i], i)
-
- ustr = u''.join(output)
-
- if encoding is None:
- return ustr
-
- return ustr.encode(encoding)
-
-
-
-def id_to_urlid(id):
- """
- convert a mql id to an id suitable for embedding in a url path.
- """
-
- # XXX shouldn't be in metaweb.api!
- from mw.formats.http import urlencode_pathseg
-
- segs = id.split('/')
-
- assert isinstance(id, str) and id != '', 'bad id "%s"' % id
-
- if id[0] == '~':
- assert len(segs) == 1
- # assume valid, should check
- return id
-
- if id[0] == '#':
- assert len(segs) == 1
- # assume valid, should check
- return '%23' + id[1:]
-
- if id[0] != '/':
- raise ValueError, 'unknown id format %s' % id
-
- # ok, we have a slash-path
- # requote components as keys and rejoin.
- # urlids do not have leading slashes!!!
- return '/'.join(urlencode_pathseg(unquotekey(seg)) for seg in segs[1:])
-
diff --git a/freebase/api/session.py b/freebase/api/session.py
deleted file mode 100644
index fc32676..0000000
--- a/freebase/api/session.py
+++ /dev/null
@@ -1,524 +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 AND CONTRIBUTORS
-# ``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 OR CONTRIBUTORS 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.
-# ========================================================================
-
-#
-# declarations for external metaweb api.
-#
-#
-# from metaweb.api import HTTPMetawebSession
-#
-# mss = HTTPMetawebSession('sandbox.freebase.com')
-# print mss.mqlread([dict(name=None, type='/type/type')])
-#
-#
-#
-
-__all__ = ['MetawebError', 'MetawebSession', 'HTTPMetawebSession', 'attrdict']
-__version__ = '0.1'
-
-import os, sys, re
-import urllib2
-import cookielib
-import simplejson
-from urllib import quote as urlquote
-import pprint
-import socket
-import logging
-
-try:
- import httplib2
- from httplib2cookie import CookiefulHttp
-except ImportError:
- httplib2 = None
- CookiefulHttp = None
- print ('freebase.api: you can install httplib2 for better performance')
-
-import simplejson.encoder
-# remove whitespace from json encoded output
-simplejson.JSONEncoder.item_separator = ','
-simplejson.JSONEncoder.key_separator = ':'
-# don't escape slashes, we're not pasting into script tags here.
-if simplejson.encoder.ESCAPE_DCT.get('/', None) == r'\/':
- simplejson.encoder.ESCAPE_DCT['/'] = '/'
-
-def urlencode_weak(s):
- return urlquote(s, safe=',/:$')
-
-
-# from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/361668
-class attrdict(dict):
- """A dict whose items can also be accessed as member variables.
-
- >>> d = attrdict(a=1, b=2)
- >>> d['c'] = 3
- >>> print d.a, d.b, d.c
- 1 2 3
- >>> d.b = 10
- >>> print d['b']
- 10
-
- # but be careful, it's easy to hide methods
- >>> print d.get('c')
- 3
- >>> d['get'] = 4
- >>> print d.get('a')
- Traceback (most recent call last):
- TypeError: 'int' object is not callable
- """
- def __init__(self, *args, **kwargs):
- dict.__init__(self, *args, **kwargs)
- self.__dict__ = self
-
-
-
-# TODO expose the common parts of the result envelope
-class MetawebError(Exception):
- """
- an error report from the metaweb service.
- """
- pass
-
-
-
-# TODO right now this is a completely unnecessary superclass.
-# is there enough common behavior between session types
-# to justify it?
-class MetawebSession(object):
- """
- MetawebSession is the base class for MetawebSession, subclassed for
- different connection types. Only http is available externally.
-
- This is more of an interface than a class
- """
-
- # interface definition here...
-
-
-# from httplib2
-NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+')
-def _normalize_headers(headers):
- return dict([ (key.lower(), NORMALIZE_SPACE.sub(value, ' ').strip()) for (key, value) in headers.iteritems()])
-
-
-class HTTPMetawebSession(MetawebSession):
- """
- a MetawebSession is a request/response queue.
-
- this version uses the HTTP api, and is synchronous.
- """
- # share cookies across sessions, so that different sessions can
- # see each other's writes immediately.
- _default_cookiejar = cookielib.CookieJar()
-
- def __init__(self, service_url, username=None, password=None, prev_session=None, cookiejar=None, cookiefile=None):
- """
- create a new MetawebSession for interacting with the Metaweb.
-
- a new session will inherit state from prev_session if present,
- """
- super(HTTPMetawebSession, self).__init__()
-
- self.log = logging.getLogger()
-
- assert not service_url.endswith('/')
- if not '/' in service_url: # plain host:port
- service_url = 'http://' + service_url
-
- self.service_url = service_url
-
- self.username = username
- self.password = password
-
- self.tid = None
-
- if prev_session:
- self.service_url = prev.service_url
-
- if cookiefile is not None:
- cookiejar = self.open_cookie_file(cookiefile)
-
- if cookiejar is not None:
- self.cookiejar = cookiejar
- elif prev_session:
- self.cookiejar = prev_session.cookiejar
- else:
- self.cookiejar = self._default_cookiejar
-
- if CookiefulHttp is not None:
- self.httpclient = CookiefulHttp(cookiejar=self.cookiejar)
- else:
- cookiespy = urllib2.HTTPCookieProcessor(self.cookiejar)
- self.opener = urllib2.build_opener(cookiespy)
-
-
- def open_cookie_file(self, cookiefile=None):
- if cookiefile is None or cookiefile == '':
- if os.environ.has_key('HOME'):
- cookiefile = os.path.join(os.environ['HOME'], '.pyfreebase/cookiejar')
- else:
- raise MetawebError("no cookiefile specified and no $HOME/.pyfreebase directory" % cookiefile)
-
- cookiejar = cookielib.LWPCookieJar(cookiefile)
- if os.path.exists(cookiefile):
- cookiejar.load(ignore_discard=True)
-
- return cookiejar
-
-
- def _httpreq(self, service_path, method='GET', body=None, form=None,
- headers=None):
- """
- make an http request to the service.
-
- form arguments are encoded in the url, even for POST, if a non-form
- content-type is given for the body.
-
- returns a pair (resp, body)
-
- resp is the response object and may be different depending
- on whether urllib2 or httplib2 is in use?
- """
-
- if method == 'POST':
- assert body is not None or form is not None
- elif method == 'GET':
- assert body is None
- else:
- assert 0, 'unknown method %s' % method
-
- url = self.service_url + service_path
-
- if headers is None:
- headers = {}
- else:
- headers = _normalize_headers(headers)
-
- # XXX This is a lousy way to parse Content-Type, where is
- # the library?
- ct = headers.get('content-type', None)
- if ct is not None:
- ct = ct.split(';')[0]
-
- if body is not None:
- # if body is provided, content-type had better be too
- assert ct is not None
-
- if form is not None:
- qstr = '&'.join(['%s=%s' % (urlencode_weak(k), urlencode_weak(v))
- for k,v in form.items()])
- if method == 'POST':
- # put the args on the url if we're putting something else
- # in the body. this is used to add args to raw uploads.
- if body is not None:
- url += '?' + qstr
- else:
- if ct is None:
- # XXX encoding and stuff
- ct = 'application/x-www-form-urlencoded'
- headers['content-type'] = ct
-
- if ct == 'multipart/form-encoded':
- # XXX fixme
- raise NotImplementedError
- elif ct == 'application/x-www-form-urlencoded':
- body = qstr
- else:
- # for all methods other than POST, use the url
- url += '?' + qstr
-
-
- # assure the service that this isn't a CSRF form submission
- headers['x-metaweb-request'] = 'Python'
-
- if 'user-agent' not in headers:
- headers['user-agent'] = 'python freebase.api-%s' % __version__
-
- #if self.tid is not None:
- # headers['x-metaweb-tid'] = self.tid
-
- ####### DEBUG MESSAGE - should check log level before generating
- if form is None:
- formstr = ''
- else:
- formstr = 'FORM:\n ' + '\n '.join(['%s=%s' % (k,v)
- for k,v in form.items()])
- if headers is None:
- headerstr = ''
- else:
- headerstr = 'HEADERS:\n ' + '\n '.join([('%s: %s' % (k,v))
- for k,v in headers.items()])
- self.log.debug('%s %s%s%s', method, url, formstr, headerstr)
- #######
-
- if CookiefulHttp is not None:
- return self._httplib2_request(url, method, body, headers)
- else:
- return self._urllib2_request(url, method, body, headers)
-
-
- def _raise_service_error(self, status, ctype, body):
- is_jsbody = (e.info().type.endswith('javascript')
- or e.info().type.endswith('json'))
- if str(status) == '400' and is_jsbody:
- r = self._loadjson(body)
- msg = r.messages[0]
- raise MetawebError(u'%s %s %r' % (msg.get('code',''), msg.message, msg.info))
-
- raise MetawebError, 'request failed: %s: %r %r' % (url, str(e), body)
-
- def _urllib2_request(self, url, method, body, headers):
- req = urllib2.Request(url, body, headers)
-
- try:
- resp = self.opener.open(req)
-
- except socket.error, e:
- self.log.error('SOCKET FAILURE: %s', e.fp.read())
- raise MetawebError, 'failed contacting %s: %s' % (url, str(e))
-
- except urllib2.HTTPError, e:
- _raise_service_error(e.code, e.info().type, e.fp.read())
-
- for header in resp.info().headers:
- self.log.debug('HTTP HEADER %s', header)
- name, value = re.split("[:\n\r]", header, 1)
- if name.lower() == 'x-metaweb-tid':
- self.tid = value.strip()
-
- return (resp, resp.read())
-
- def _httplib2_request(self, url, method, body, headers):
- try:
- resp, content = self.httpclient.request(url, method=method,
- body=body, headers=headers)
- except socket.error, e:
- self.log.error('SOCKET FAILURE: %s', e.fp.read())
- raise MetawebError, 'failed contacting %s: %s' % (url, str(e))
-
- except httplib2.HttpLib2ErrorWithResponse, e:
- self._raise_service_error(resp.status, resp['content-type'], content)
- except httplib2.HttpLib2Error, e:
- raise MetawebError(u'HTTP error: %s' % (e,))
-
- #tid = resp.get('x-metaweb-tid', None)
-
- return (resp, content)
-
-
- def _httpreq_json(self, *args, **kws):
- resp, body = self._httpreq(*args, **kws)
- return self._loadjson(body)
-
- def _loadjson(self, json):
- # TODO really this should be accomplished by hooking
- # simplejson to create attrdicts instead of dicts.
- def struct2attrdict(st):
- """
- copy a json structure, turning all dicts into attrdicts.
-
- copying descends instances of dict and list, including subclasses.
- """
- if isinstance(st, dict):
- return attrdict([(k,struct2attrdict(v)) for k,v in st.items()])
- if isinstance(st, list):
- return [struct2attrdict(li) for li in st]
- return st
-
- if json == '':
- self.log.error('the empty string is not valid json')
- raise MetawebError('the empty string is not valid json')
-
- try:
- r = simplejson.loads(json)
- except ValueError, e:
- self.log.error('error parsing json string %r' % json)
- raise MetawebError, 'error parsing JSON string: %s' % e
-
- return struct2attrdict(r)
-
- def _check_mqlerror(self, r):
- if r.code != '/api/status/ok':
- for msg in r.messages:
- self.log.error('mql error: %s %s %r' % (msg.code, msg.message, msg.get('query', None)))
- raise MetawebError, 'query failed: %s %r' % (r.messages[0].code, r.messages[0].get('query', None))
-
- def _mqlresult(self, r):
- self._check_mqlerror(r)
-
- # should check log level to avoid redundant simplejson.dumps
- rstr = simplejson.dumps(r.result, indent=2)
- if rstr[0] == '{':
- rstr = rstr[1:-2]
- self.log.info('result: %s', rstr)
-
- return r.result
-
-
-
- def login(self):
- """sign in to the service"""
-
- assert self.username is not None
- assert self.password is not None
-
- self.log.debug('LOGIN USERNAME: %s', self.username)
-
- try:
- r = self._httpreq_json('/api/account/login', 'POST',
- form=dict(username=self.username,
- password=self.password))
- except urllib2.HTTPError, e:
- raise MetawebError("login error: %s", e)
-
- if r.code != '/api/status/ok':
- raise MetawebError(u'%s %r' % (r.get('code',''), r.messages))
-
- self.log.debug('LOGIN RESP: %r', r)
- self.log.debug('LOGIN COOKIES: %s', self.cookiejar)
-
-
- def mqlreaditer(self, sq):
- """read a structure query"""
-
- cursor = True
-
- while 1:
- subq = dict(query=[sq], cursor=cursor, escape=False)
- qstr = simplejson.dumps(subq)
-
- service = '/api/service/mqlread'
-
- r = self._httpreq_json(service, form=dict(query=qstr))
-
- for item in self._mqlresult(r):
- yield item
-
- if r['cursor']:
- cursor = r['cursor']
- self.log.info('CONTINUING with %s', cursor)
- else:
- return
-
- def mqlread(self, sq):
- """read a structure query"""
- subq = dict(query=sq, escape=False)
- if isinstance(sq, list):
- subq['cursor'] = True
-
- service = '/api/service/mqlread'
-
- # should check log level to avoid redundant simplejson.dumps
- self.log.info('%s: %s',
- service,
- simplejson.dumps(sq, indent=2)[1:-2])
-
- qstr = simplejson.dumps(subq)
- r = self._httpreq_json(service, form=dict(query=qstr))
-
- return self._mqlresult(r)
-
- def trans(self, guid):
- """translate blob from guid """
- url = '/api/trans/raw' + urlquote(guid)
-
- self.log.info(url)
-
- resp, body = self._httpreq(url)
-
- self.log.info('%d bytes' % len(body))
-
- return body
-
- def mqlwrite(self, sq):
- """do a mql write"""
- query = dict(query=sq, escape=False)
- qstr = simplejson.dumps(query)
-
- self.log.debug('MQLWRITE: %s', qstr)
-
- service = '/api/service/mqlwrite'
-
- # should check log level to avoid redundant simplejson.dumps
- self.log.info('%s: %s',
- service,
- simplejson.dumps(sq, indent=2)[1:-2])
-
- r = self._httpreq_json(service, 'POST',
- form=dict(query=qstr))
-
- self.log.debug('MQLWRITE RESP: %r', r)
- return self._mqlresult(r)
-
- def mqlflush(self):
- """ask the service not to hand us old data"""
- self.log.debug('MQLFLUSH')
-
- service = '/api/service/mqlwrite'
- r = self._httpreq_json(service, 'POST', form={})
-
- self._check_mqlerror(r)
- return r
-
- def upload(self, body, content_type, document_id=False):
- """upload to the metaweb"""
-
- service = '/api/service/upload'
-
- self.log.info('POST %s: %s (%d bytes)',
- service, content_type, len(body))
-
-
- headers = {}
- if content_type is not None:
- headers['content-type'] = content_type
-
- form = None
- if document_id is not False:
- if document_id is None:
- form = { 'document': '' }
- else:
- form = { 'document': document_id }
-
- # note the use of both body and form.
- # form parameters get encoded into the URL in this case
- r = self._httpreq_json(service, 'POST',
- headers=headers, body=body, form=form)
- return self._mqlresult(r)
-
-
-if __name__ == '__main__':
- console = logging.StreamHandler()
- console.setLevel(logging.DEBUG)
-
- mss = HTTPMetawebSession('sandbox.freebase.com')
-
- self.mss.log.setLevel(logging.DEBUG)
- self.mss.log.addHandler(console)
-
-
- print mss.mqlread([dict(name=None, type='/type/type')])