commit
43f656f296
|
@ -1,3 +1,8 @@
|
|||
3.0
|
||||
===
|
||||
|
||||
Changed to using dictionaries instead of parsing XML.
|
||||
|
||||
2.4
|
||||
===
|
||||
|
||||
|
|
26
README.rst
26
README.rst
|
@ -8,15 +8,15 @@ v2.0 API. This project is hosted on `Github
|
|||
Installation
|
||||
============
|
||||
|
||||
This library is released to PyPI, so the easiest way to install it is to use
|
||||
easy_install::
|
||||
|
||||
easy_install wolframalpha
|
||||
|
||||
or pip::
|
||||
This library is released to PyPI - the Python Package Index, so the easiest way to install it is to use
|
||||
pip::
|
||||
|
||||
pip install wolframalpha
|
||||
|
||||
or easy_install::
|
||||
|
||||
easy_install wolframalpha
|
||||
|
||||
If you don't have these tools or you prefer not to use setuptools, you may
|
||||
also simply extract the 'wolframalpha' directory an appropriate location in
|
||||
your Python path.
|
||||
|
@ -34,13 +34,21 @@ Then, you can send queries, which return Result objects::
|
|||
|
||||
res = client.query('temperature in Washington, DC on October 3, 2012')
|
||||
|
||||
Result objects have `pods` attribute (a Pod is an answer from Wolfram Alpha)::
|
||||
Result objects have `pods` (a Pod is an answer group from Wolfram Alpha)::
|
||||
|
||||
for pod in res.pods:
|
||||
do_something_with(pod)
|
||||
|
||||
You may also query for simply the pods which have 'Result' titles::
|
||||
Pod objects have `subpods` (a Subpod is a specific response with the plaintext reply and some additional info)::
|
||||
|
||||
print(next(res.results).text)
|
||||
for pod in res.pods:
|
||||
for sub in pod:
|
||||
print(sub.text)
|
||||
|
||||
You may also query for simply the pods which have 'Result' titles or are marked as 'primary'::
|
||||
|
||||
print(res.results[0].text[0])
|
||||
|
||||
The interface as it is now does not have code built for accessing every piece of information that the Wolfram Alpha API could return. As such, every class has a copy of the original structure that it is supposed to parse. This copy is placed in a variable called node for every class but the Result class, whose variable is named tree. If there is information from the Wolfram Alpha API that you need for your program that this interface does not provide an exact function for then you can still gain access to that information through the previously mentioned variables; you'll just have to handle the API directly until the functionality you seek is built.
|
||||
|
||||
For more information, read the source.
|
||||
|
|
1
setup.py
1
setup.py
|
@ -33,6 +33,7 @@ setup_params = dict(
|
|||
namespace_packages=name.split('.')[:-1],
|
||||
install_requires=[
|
||||
'six',
|
||||
'xmltodict',
|
||||
],
|
||||
extras_require={
|
||||
},
|
||||
|
|
|
@ -1,84 +1,184 @@
|
|||
from xml.etree import ElementTree as etree
|
||||
from six.moves import urllib
|
||||
import xmltodict
|
||||
|
||||
from . import compat
|
||||
|
||||
compat.fix_HTTPMessage()
|
||||
|
||||
class Result(object):
|
||||
def __init__(self, stream):
|
||||
self.tree = etree.parse(stream)
|
||||
self._handle_error()
|
||||
|
||||
def _handle_error(self):
|
||||
error = self.tree.find('error')
|
||||
if not error:
|
||||
return
|
||||
|
||||
code = error.find('code').text
|
||||
msg = error.find('msg').text
|
||||
tmpl = 'Error {code}: {msg}'
|
||||
raise Exception(tmpl.format(code=code, msg=msg))
|
||||
|
||||
def __iter__(self):
|
||||
return (Pod(node) for node in self.tree.findall('pod'))
|
||||
|
||||
def __len__(self):
|
||||
return len(self.tree)
|
||||
|
||||
@property
|
||||
def pods(self):
|
||||
return list(iter(self))
|
||||
|
||||
@property
|
||||
def results(self):
|
||||
return (pod for pod in self if pod.title=='Result')
|
||||
|
||||
class Pod(object):
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.__dict__.update(node.attrib)
|
||||
|
||||
def __iter__(self):
|
||||
return (Content(node) for node in self.node.findall('subpod'))
|
||||
|
||||
@property
|
||||
def main(self):
|
||||
"The main content of this pod"
|
||||
return next(iter(self))
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self.main.text
|
||||
|
||||
@property
|
||||
def img(self):
|
||||
return self.main.img
|
||||
|
||||
class Content(object):
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.__dict__.update(node.attrib)
|
||||
self.text = node.find('plaintext').text
|
||||
self.img = node.find('img').attrib['src']
|
||||
|
||||
class Client(object):
|
||||
"""
|
||||
Wolfram|Alpha v2.0 client
|
||||
|
||||
Pass an ID to the object upon instantiation, then
|
||||
query Wolfram Alpha using the query method.
|
||||
"""
|
||||
def __init__(self, app_id):
|
||||
def __init__(self, app_id='Q59EW4-7K8AHE858R'):
|
||||
self.app_id = app_id
|
||||
|
||||
def query(self, query):
|
||||
def query(self, query, assumption=None):
|
||||
"""
|
||||
Query Wolfram|Alpha with query using the v2.0 API
|
||||
Query Wolfram|Alpha using the v2.0 API
|
||||
Allows for assumptions to be included.
|
||||
See: http://products.wolframalpha.com/api/documentation.html#6
|
||||
"""
|
||||
query = urllib.parse.urlencode(dict(
|
||||
input=query,
|
||||
appid=self.app_id,
|
||||
))
|
||||
data = {
|
||||
'input': query,
|
||||
'appid': self.app_id
|
||||
}
|
||||
if assumption:
|
||||
data.update({'assumption': assumption})
|
||||
|
||||
query = urllib.parse.urlencode(data)
|
||||
url = 'https://api.wolframalpha.com/v2/query?' + query
|
||||
resp = urllib.request.urlopen(url)
|
||||
assert resp.headers.get_content_type() == 'text/xml'
|
||||
assert resp.headers.get_param('charset') == 'utf-8'
|
||||
return Result(resp)
|
||||
|
||||
class Result(object):
|
||||
'''
|
||||
Handles processing the response for the programmer.
|
||||
'''
|
||||
def __init__(self, stream):
|
||||
self.tree = xmltodict.parse(stream, dict_constructor=dict)['queryresult']
|
||||
self._handle_error()
|
||||
self.info = []
|
||||
try:
|
||||
self.pods = list(map(Pod, self.tree['pod']))
|
||||
self.info.append(self.pods)
|
||||
except KeyError:
|
||||
self.pods = None
|
||||
try:
|
||||
self.assumptions = list(map(Assumption, self.tree['assumptions']))
|
||||
self.info.append(self.assumptions)
|
||||
except KeyError:
|
||||
self.assumptions = None
|
||||
try:
|
||||
self.warnings = list(map(Warning, self.tree['warnings']))
|
||||
self.info.append(self.warnings)
|
||||
except KeyError:
|
||||
self.warnings = None
|
||||
|
||||
def _handle_error(self):
|
||||
error_state = self.tree['@error']
|
||||
if error_state == 'false':
|
||||
return
|
||||
|
||||
error = self.tree['error']
|
||||
code = error['code']
|
||||
msg = error['msg']
|
||||
template = 'Error {code}: {msg}'
|
||||
raise Exception(template.format(code=code, msg=msg))
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.info)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.info)
|
||||
|
||||
@property
|
||||
def results(self):
|
||||
''' Get the pods that hold the response to a simple, discrete query. '''
|
||||
return [pod for pod in self.pods if pod.primary or pod.title=='Result']
|
||||
|
||||
@property
|
||||
def details(self):
|
||||
''' Get a simplified set of answers with some context. '''
|
||||
return {pod.title: pod.text for pod in self.pods}
|
||||
|
||||
class Pod(object):
|
||||
''' Groups answers and information contextualizing those answers. '''
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.error = node['@error']
|
||||
self._handle_error()
|
||||
self.title = node['@title']
|
||||
self.scanner = node['@scanner']
|
||||
self.id = node['@id']
|
||||
self.position = float(node['@position'])
|
||||
self.number_of_subpods = int(node['@numsubpods'])
|
||||
self.subpods = node['subpod']
|
||||
# Allow subpods to be accessed in a consistent way,
|
||||
# as a list, regardless of how many there are.
|
||||
if type(self.subpods) != list:
|
||||
self.subpods = [self.subpods]
|
||||
self.subpods = list(map(Subpod, self.subpods))
|
||||
self.primary = '@primary' in node and node['@primary'] != 'false'
|
||||
|
||||
def _handle_error(self):
|
||||
if self.error == 'false':
|
||||
return
|
||||
|
||||
error = self.node['error']
|
||||
code = error['code']
|
||||
msg = error['msg']
|
||||
template = 'Error {code}: {msg}'
|
||||
raise Exception(template.format(code=code, msg=msg))
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.subpods)
|
||||
|
||||
def __len__(self):
|
||||
return self.number_of_subpods
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
''' Simply get the text from each subpod in this pod and return it in a list. '''
|
||||
return [subpod.text for subpod in self.subpods]
|
||||
|
||||
class Subpod(object):
|
||||
''' Holds a specific answer or additional information relevant to said answer. '''
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.title = node['@title']
|
||||
self.text = node['plaintext']
|
||||
self.img = node['img']
|
||||
# Allow images to be accessed in a consistent way,
|
||||
# as a list, regardless of how many there are.
|
||||
if type(self.img) != list:
|
||||
self.img = [self.img]
|
||||
self.img = list(map(Image, self.img))
|
||||
|
||||
# Needs work. At the moment this should be considered a placeholder.
|
||||
class Assumption(object):
|
||||
def __init__(self, node):
|
||||
self.assumption = node
|
||||
#self.description = self.assumption[0]['desc'] # We only care about our given assumption.
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.assumption)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.assumption)
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
text = self.template.replace('${desc1}', self.description)
|
||||
try:
|
||||
text = text.replace('${word}', self.word)
|
||||
except:
|
||||
pass
|
||||
return text[:text.index('. ') + 1]
|
||||
|
||||
# Needs work. At the moment this should be considered a placeholder.
|
||||
class Warning(object):
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
|
||||
def __iter__(self):
|
||||
return iter(node)
|
||||
|
||||
def __len__(self):
|
||||
return len(node)
|
||||
|
||||
class Image(object):
|
||||
''' Holds information about an image included with an answer. '''
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.title = node['@title']
|
||||
self.alt = node['@alt']
|
||||
self.height = node['@height']
|
||||
self.width = node['@width']
|
||||
self.src = node['@src']
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ def test_basic(API_key):
|
|||
res = client.query('30 deg C in deg F')
|
||||
assert len(res.pods) > 0
|
||||
results = list(res.results)
|
||||
assert results[0].text == '86 °F (degrees Fahrenheit)'
|
||||
assert results[0].text == ['86 °F (degrees Fahrenheit)']
|
||||
|
||||
def test_invalid_app_id():
|
||||
client = wolframalpha.Client('abcdefg')
|
||||
|
|
Loading…
Reference in New Issue
Block a user