Merge pull request #7 from Zenohm/master

Restructuring
master
Jason R. Coombs 8 years ago committed by GitHub
commit 43f656f296
  1. 5
      CHANGES.rst
  2. 24
      README.rst
  3. 1
      setup.py
  4. 208
      wolframalpha/__init__.py
  5. 2
      wolframalpha/test_client.py

@ -1,3 +1,8 @@
3.0
===
Changed to using dictionaries instead of parsing XML.
2.4
===

@ -8,14 +8,14 @@ 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::
This library is released to PyPI - the Python Package Index, so the easiest way to install it is to use
pip::
easy_install wolframalpha
pip install wolframalpha
or pip::
or easy_install::
pip install wolframalpha
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
@ -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)::
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])
print(next(res.results).text)
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.

@ -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 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='Q59EW4-7K8AHE858R'):
self.app_id = app_id
def query(self, query, assumption=None):
"""
Query Wolfram|Alpha using the v2.0 API
Allows for assumptions to be included.
See: http://products.wolframalpha.com/api/documentation.html#6
"""
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 = etree.parse(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 = self.tree.find('error')
if not error:
error_state = self.tree['@error']
if error_state == 'false':
return
code = error.find('code').text
msg = error.find('msg').text
tmpl = 'Error {code}: {msg}'
raise Exception(tmpl.format(code=code, msg=msg))
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 (Pod(node) for node in self.tree.findall('pod'))
return iter(self.info)
def __len__(self):
return len(self.tree)
@property
def pods(self):
return list(iter(self))
return len(self.info)
@property
def results(self):
return (pod for pod in self if pod.title=='Result')
''' 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.__dict__.update(node.attrib)
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 (Content(node) for node in self.node.findall('subpod'))
@property
def main(self):
"The main content of this pod"
return next(iter(self))
return iter(self.subpods)
def __len__(self):
return self.number_of_subpods
@property
def text(self):
return self.main.text
''' 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 img(self):
return self.main.img
class Content(object):
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
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
"""
def __init__(self, app_id):
self.app_id = app_id
def __iter__(self):
return iter(node)
def __len__(self):
return len(node)
def query(self, query):
"""
Query Wolfram|Alpha with query using the v2.0 API
"""
query = urllib.parse.urlencode(dict(
input=query,
appid=self.app_id,
))
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 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…
Cancel
Save