Skip to content

Instantly share code, notes, and snippets.

@jdnc
Last active December 18, 2015 08:00
Show Gist options
  • Save jdnc/5751109 to your computer and use it in GitHub Desktop.
Save jdnc/5751109 to your computer and use it in GitHub Desktop.
core.py of irsa_dust module. A very 'primitive' version. also utils.py that has some helper functions. only the functions now used in core.py have been retained.
# Licensed under a 3-clause BSD style license - see LICENSE.rst
import types
import functools
import requests
from xml.etree.ElementTree import ElementTree
from astropy.table import Table, Column
import astropy.units as u
from astropy.io import fits
import utils
import astropy.utils.data as aud
import astropy.coordinates as coord
from astropy import log
# TODO Add support for server url from JSON cache
__all__ = ["IrsaDust"]
DUST_SERVICE_URL = "http://irsa.ipac.caltech.edu/cgi-bin/DUST/nph-dust"
EXT_DESC = "E(B-V) Reddening"
EM_DESC = "100 Micron Emission"
TEMP_DESC = "Dust Temperature"
INPUT = "input"
OBJ_NAME = "objname"
REG_SIZE = "regSize"
DESC = "desc"
IMAGE_URL = "imageUrl"
STATISTICS = "statistics"
REF_PIXEL_VALUE = "refPixelValue"
REF_COORDINATE = "refCoordinate"
MEAN_VALUE = "meanValue"
STD = "std"
MAX_VALUE = "maxValue"
MIN_VALUE = "minValue"
DATA_IMAGE = "./data/image"
DATA_TABLE = "./data/table"
TIMEOUT = 30 # timeout in seconds
# support for classmethod overloading
class static_or_instance(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return functools.partial(self.func, instance)
# Needs to be in a separate module
class TimeoutError(Exception):
pass
class InvalidQueryError(Exception):
pass
class QueryClass(object):
def login(self, *args):
pass
def __call__(self):
raise Exception("All classes must override this!")
class IrsaDust(QueryClass):
def __init__(self, *args):
self.coordinate = ""
@static_or_instance
def get_images(self, coordinate, radius=None, timeout=TIMEOUT, get_query_payload=False):
"""
gets the image object
returns list of astropy.io.fits.HDUList`s
"""
if get_query_payload:
return self.args_to_payload(coordinate, radius=radius)
readable_objs = self.get_images_async(coordinate, radius=radius, timeout=timeout,
get_query_payload=get_query_payload)
return [fits.open(obj.__enter__()) for obj in readable_objs]
@static_or_instance
def get_images_async(self, coordinate, radius=None, timeout=TIMEOUT, get_query_payload=False):
"""
gets the handler but doesn't download the image object
"""
if get_query_payload:
return self.args_to_payload(coordinate, radius=radius)
image_urls = self.get_image_list(coordinate, radius=radius, timeout=timeout)
return [aud.get_readable_fileobj(U) for U in image_urls]
@static_or_instance
def get_image_list(self, coordinate, radius=None, timeout=TIMEOUT):
"""
Returns list of urls to FITS images
"""
request_payload = self.args_to_payload(coordinate, radius=radius)
try:
response = requests.post(DUST_SERVICE_URL, data=request_payload, timeout=timeout)
except requests.exceptions.Timeout:
raise TimeoutError("Query timed out, time elapsed {time}s".format(time=timeout))
except requests.exceptions.RequestException:
raise
return self.extract_image_urls(response.text)
@static_or_instance
def get_section_image(self, coordinate, radius=None, timeout=TIMEOUT, section=None):
"""
get only the images for one section - location/emission/reddening
"""
readable_objs = self.get_section_image_async(coordinate, radius=radius, timeout
=timeout, section=section)
return [fits.open(obj.__enter__()) for obj in readable_objs]
@static_or_instance
def get_section_image_async(self, coordinate, radius=None, timeout=TIMEOUT, section=None):
"""
get the handlers only for section image
"""
request_payload = self.args_to_payload(coordinate, radius=radius)
try:
result = requests.post(DUST_SERVICE_URL, data=request_payload, timeout=timeout)
except requests.exceptions.Timeout:
raise TimeoutError("Query timed out, time elapsed {time}s".format(time=timeout))
except requests.exceptions.RequestException:
raise
image_urls = self.extract_image_urls(result.text, section=section)
return [aud.get_readable_fileobj(U) for U in image_urls]
@static_or_instance
def get_ext_table(self, coordinate, radius=None, timeout=TIMEOUT):
"""
get the extinction table as astropy.Table
"""
readable_obj = self.get_ext_table_async(coordinate, radius=radius, timeout=timeout)
table = Table.read(readable_obj.__enter__(), format='ipac')
return table
@static_or_instance
def get_ext_table_async(self, coordinate, radius=None, timeout=TIMEOUT):
"""
get handler to extinction table
"""
request_payload = self.args_to_payload(coordinate, radius=radius)
try:
response = requests.post(DUST_SERVICE_URL, data=request_payload, timeout=timeout)
except requests.exceptions.Timeout:
raise TimeoutError("Query timed out, time elapsed {time}s".format(time=timeout))
except requests.exceptions.RequestException:
raise
xml_tree = utils.xml(response.text)
result = SingleDustResult(xml_tree, coordinate)
return aud.get_readable_fileobj(result.ext_detail_table())
@static_or_instance
def get_query_table(self, coordinate, radius=None, section=None, timeout=TIMEOUT):
"""
get the xml response of query as astropy.Table
"""
request_payload = self.args_to_payload(coordinate, radius=radius)
try:
response = requests.post(DUST_SERVICE_URL, data=request_payload, timeout=timeout)
except requests.exceptions.Timeout:
raise TimeoutError("Query timed out, time elapsed {time}s".format(time=timeout))
except requests.exceptions.RequestException:
raise
xml_tree = utils.xml(response.text)
result = SingleDustResult(xml_tree, coordinate)
return result.table(section=section)
def args_to_payload(self, coordinate, radius=None):
"""
convert the query parameters to dict
"""
payload = {"locstr" : coordinate} # check if this is resolvable?
# check if radius is given with proper units
if radius != None:
try:
a = coord.Angle(radius)
reg_size = a.degrees
except u.UnitsException as ex:
raise Exception("Radius not specified with proper unit.")
# check if radius falls in the acceptable range
if reg_size < 2 or reg_size > 37.5:
raise ValueError("Radius (in any unit) must be in the"
"range of 2.0 to 37.5 degrees")
payload["regSize"] = reg_size
# also assign this to a coordinate attribute
self.coordinate = coordinate
return payload
def extract_image_urls(self, raw_xml, section=None):
"""
extract the image urls from the raw xml response
return list of urls
"""
# get the xml tree from the response
xml_tree = utils.xml(raw_xml)
result = SingleDustResult(xml_tree, self.coordinate)
if section is None or "all":
url_list = [result.image(sec) for sec in
['r', 'e', 't']]
else:
url_list = [result.image(section)]
return url_list
class SingleDustResult(object):
"""
Represents the response to a dust query for a single object or location.
Provides methods to return the response as an astropy Table, and to retrieve
FITS images listed as urls in the initial response. It can also retrieve
a detailed extinction table linked to in the initial response. Not intended
to be instantiated by the end user.
"""
def __init__(self, xml_tree, query_loc):
"""
Parameters
----------
xml_root : `xml.etree.ElementTree`
the xml tree representing the response to the query
query_loc : str
the location string specified in the query
"""
self._xml = xml_tree
self._query_loc = query_loc
self._location_section = LocationSection(xml_tree)
ext_node = utils.find_result_node(EXT_DESC, xml_tree)
self._ext_section = ExtinctionSection(ext_node)
em_node = utils.find_result_node(EM_DESC, xml_tree)
self._em_section = EmissionSection(em_node)
temp_node = utils.find_result_node(TEMP_DESC, xml_tree)
self._temp_section = TemperatureSection(temp_node)
self._result_sections = [self._location_section, self._ext_section,
self._em_section, self._temp_section]
@property
def query_loc(self):
"""Return the location string used in the query."""
return self._query_loc
@property
def xml(self):
"""Return the raw xml underlying this SingleDustResult."""
return self._xml
def table(self, section=None):
"""
Create and return an astropy Table representing the query response.
Parameters
----------
section : str
(optional) the name of the section to include in the table.
If not provided, the entire table will be returned.
"""
code = self._section_code(section)
if code == "all":
return self._table_all()
else:
return self._table(code)
def values(self, section=None):
"""
Return the data values contained in the query response,
i.e. the list of values corresponding to a row in the result table.
Parameters
----------
section : str
the name of the section to include in the response
If no section is given, all sections will be included.
"""
code = self._section_code(section)
sections = self._sections(code)
values = []
for sec in sections:
values.extend(sec.values())
return values
def _section_code(self, section):
"""
Return a one-letter code identifying the section.
Parameters
----------
section : str
the name or abbreviated name of the section
Returns
-------
str: a one-letter code identifying the section.
"""
if section == None:
return "all"
else:
if section in ["location", "loc", "l"]:
return "l"
elif section in ["reddening", "red", "r"]:
return "r"
elif section in ["emission", "em", "e"]:
return "e"
elif section in ["temperature", "temp", "t"]:
return "t"
else:
msg = """section must be one of the following:
'all',
'location', 'loc', 'l',
'reddening', 'red', 'r',
'emission', 'em', 'e',
'temperature', 'temp', 't'."""
raise ValueError(msg)
def _sections(self, code):
"""
Parameters
----------
code : str
the one-letter code name of the section
Returns
-------
The section corresponding to the code, or a list containing all sections if
no section is provided.
"""
if code == 'l':
return [self._location_section]
elif code == 'r':
return [self._ext_section]
elif code == 'e':
return [self._em_section]
elif code == 't':
return [self._temp_section]
return [self._location_section, self._ext_section, self._em_section, self._temp_section]
def _table_all(self):
"""
Create and return the full table containing all four sections:
location, extinction, emission, and temperature.
Returns
-------
table : `astropy.table.Table`
table containing the data from the query response
"""
columns = (self._location_section.columns + self._ext_section.columns
+ self._em_section.columns + self._temp_section.columns)
table = Table(data=columns)
values = self.values()
table.add_row(vals=values)
return table
def _table(self, section):
"""
Create and return a smaller table containing only one section
of the overall DustResult table.
Parameters
----------
section : str
a string indicating the section to be returned
"""
# Get the specified section
section_object = self._sections(section)[0]
# Create the table
columns = section_object.columns
table = Table(data=columns)
# Populate the table
values = section_object.values()
table.add_row(vals=values)
return table
def ext_detail_table(self):
"""
Get the url of the additional, detailed table of extinction data for various filters.
There is a url for this table given in the initial response to the query.
Returns
-------
table_url : str
url of the detailed table of extinction data by filter
"""
table_url = self._ext_section.table_url
#response = utils.ext_detail_table(table_url)
#if sys.version_info > (3, 0):
# read_response = response.read().decode("utf-8")
#else:
# read_response = response.read()
#table = Table.read(read_response, format="ipac")
#return table
return table_url
def image(self, section):
"""
Get the FITS image url associated with the given section.
The extinction, emission, and temperature sections each provide
a url to a FITS image.
Parameters
----------
section : str
the name of the section
Returns
-------
url : str
the url to the FITS image
"""
# Get the url of the image for the given section
image_url = None
if section in ["reddening", "red", "r"]:
image_url = self._ext_section.image_url
elif section in ["emission", "em", "e"]:
image_url = self._em_section.image_url
elif section in ["temperature", "temp", "t"]:
image_url = self._temp_section.image_url
if image_url == None:
msg = """section must be one of the following values:
'reddening', 'red', 'r',
'emission', 'em', 'e',
'temperature', 'temp', 't'"""
raise ValueError(msg)
#response = utils.image(image_url)
#S = io.BytesIO(response)
#image = fits.open(S)
return image_url
def __str__(self):
"""Return a string representation of the table."""
string = "[DustResult: \n\t"
for section in self._result_sections:
if len(string) > 15:
string += ",\n\t"
string += section.__str__()
string += "]"
return string
class BaseDustNode(object):
"""
A node in the result xml that has been enhanced to return values and Columns
appropriate to its type (String, Number, or Coord).
"""
def __init__(self, xml_node):
"""
Parameters
----------
xml_node : `xml.etree.ElementTree`
the xml node that provides the raw data for this DustNode
"""
self._name = xml_node.tag
def set_value(self, node_text):
"""Override in subclasses."""
pass
@property
def name(self):
"""Return the xml node name."""
return self._name
@property
def value(self):
"""Return the value extracted from the node."""
return self._value
@property
def columns(self):
"""Return the column or columns associated with this item in the astropy Table."""
return self._columns
def __str__(self):
"""Return a string representation of this item."""
col_str = "[Column: "
for column in self._columns:
for format_str in column.pformat(show_units=True):
col_str += format_str
string = "name: " + self._name + ", " + col_str + "]"
return string
class StringNode(BaseDustNode):
"""
A node that contains text.
"""
def __init__(self, xml_node, col_name, length):
"""
Parameters
----------
xml_node : `xml.etree.ElementTree`
the xml node that provides the raw data for this DustNode
col_name : str
the name of the column associated with this item
length : int
the size of the column associated with this item
"""
BaseDustNode.__init__(self, xml_node)
self._value = xml_node.text.strip()
self._length = length
self._columns = [Column(name=col_name, dtype="S" + str(length))]
def __str__(self):
"""Return a string representation of this item."""
base_string = BaseDustNode.__str__(self)
string = ("[StringNode: " + base_string
+ ", value: " + self._value + "]")
return string
class NumberNode(BaseDustNode):
"""
A node that contains a number. Outputs a single column containing the number.
"""
def __init__(self, xml_node, col_name, units=None):
"""
Parameters
----------
xml_node : `xml.etree.ElementTree`
the xml node that provides the raw data for this DustNode
col_name : str
the name of the column associated with this item
units : `astropy.units.Unit`
the units associated with this item
"""
BaseDustNode.__init__(self, xml_node)
self._value = utils.parse_number(xml_node.text)
self._columns = [Column(name=col_name, units=units)]
def __str__(self):
"""Return a string representation of the item."""
base_string = BaseDustNode.__str__(self)
string = ("[NumberNode: " + base_string
+ ", value: " + str(self._value) + "]")
return string
class CoordNode(BaseDustNode):
"""
A node that contains RA, Dec coordinates. Outputs three values / columns: RA, Dec
and a coordinate system description string.
"""
def __init__(self, xml_node, col_names):
"""
Parameters
----------
xml_node : `xml.etree.ElementTree`
the xml node that provides the raw data for this DustNode
col_names : str
the names of the columns associated with this item
"""
BaseDustNode.__init__(self, xml_node)
self._value = utils.parse_coords(xml_node.text)
units = u.deg
self._columns = [Column(name=col_names[0], units=units),
Column(name=col_names[1], units=units),
Column(name=col_names[2], dtype="S25")]
def __str__(self):
"""Return a string representation of the item."""
base_string = BaseDustNode.__str__(self)
values_str = ("values: " + str(self._value[0]) + ", " + str(self._value[1])
+ ", " + str(self._value[2]))
string = ("[CoordNode: " + base_string + ", " + values_str + "]")
return string
class BaseResultSection(object):
"""
Represents a group of related nodes/columns in a DustResults object.
A DustResults table contains four main sections:
1-location
2-extinction
3-emission
4-temperature
In addition, the extinction, emission, and temperature sections
each contain a nested statistics subsection.
"""
def node_dict(self, names, xml_root):
"""
Find all the nodes with the given names under the given root,
and put them in a dictionary.
Parameters
----------
names : list[str]
the names of the nodes to find
xml_root : `xml.etree.ElementTree`
the root of the xml tree to search
Returns
-------
nodes : dictionary
a dictionary of xml nodes, where the keys are the node names
"""
nodes = {}
for name in names:
found_node = xml_root.find(name)
if found_node == None:
raise ValueError("Could not find node '" + name + "'")
nodes[name] = found_node
return nodes
def create_columns(self):
"""Build the columns associated with this section."""
columns = []
for dust_node in self._dust_nodes:
if isinstance(dust_node._columns, types.ListType):
columns.extend(dust_node._columns)
else:
columns.append(dust_node._columns)
self._columns = columns
@property
def columns(self):
"""Return the list of columns associated with this section."""
return self._columns
def values(self):
"""Return the list of data values associated with this section,
i.e. the data corresponding to a single row in the results table."""
values = []
for dust_node in self._dust_nodes:
if isinstance(dust_node._value, types.ListType):
values.extend(dust_node._value)
else:
values.append(dust_node._value)
return values
def __str__(self):
"""Return a string representation of the section."""
string = "\n\t\t"
for dust_node in self._dust_nodes:
if len(string) > 6:
string += ",\n\t\t"
string += dust_node.__str__()
return string
class LocationSection(BaseResultSection):
"""
The location section of the DustResults object.
"""
def __init__(self, xml_root):
"""
Parameters
----------
xml_root : `xml.etree.ElementTree`
the xml tree where the data for this section resides
"""
location_node = xml_root.find(INPUT)
names = [OBJ_NAME, REG_SIZE]
xml_nodes = self.node_dict(names, location_node)
# Create the section's DustNodes
self._dust_nodes = [CoordNode(xml_nodes[OBJ_NAME], col_names=["RA", "Dec", "coord sys"]),
NumberNode(xml_nodes[REG_SIZE], REG_SIZE, u.deg)]
self.create_columns()
def __str__(self):
"""Return a string representation of the section."""
base_string = BaseResultSection.__str__(self)
string = "[LocationSection: " + base_string + "]"
return string
class StatsSection(BaseResultSection):
"""
The statistics subsection of one of an extinction, emission, or temperature
section.
"""
def __init__(self, xml_root, col_prefix):
"""
Parameters
----------
xml_root : `xml.etree.ElementTree`
The xml tree containing the data for this section
col_prefix : str
the prefix to use in column names for this section
"""
names = [REF_PIXEL_VALUE, REF_COORDINATE, MEAN_VALUE, STD, MAX_VALUE, MIN_VALUE]
xml_nodes = self.node_dict(names, xml_root)
# Create the DustNodes
self._dust_nodes = [NumberNode(xml_nodes[REF_PIXEL_VALUE], col_prefix + " ref"),
CoordNode(xml_nodes[REF_COORDINATE], col_names=[col_prefix + " ref RA",
col_prefix + " ref Dec", col_prefix + " ref coord sys"]),
NumberNode(xml_nodes[MEAN_VALUE], col_prefix + " mean"),
NumberNode(xml_nodes[STD], col_prefix + " std"),
NumberNode(xml_nodes[MAX_VALUE], col_prefix + " max"),
NumberNode(xml_nodes[MIN_VALUE], col_prefix + " min")]
self._units = utils.parse_units(xml_nodes[REF_PIXEL_VALUE].text)
self.create_columns()
@property
def units(self):
"""Return the units associated with this section."""
return self._units
@property
def dust_nodes(self):
"""Return the list of DustNodes in this section."""
return self._dust_nodes
def __str__(self):
"""Return a string representation of the section."""
base_string = "\n\t\t\t\t"
for dust_node in self._dust_nodes:
if len(base_string) > 6:
base_string += ",\n\t\t\t\t"
base_string += dust_node.__str__()
string = "\n\t\t\t[StatisticsSection: " + base_string + "]"
return string
class ExtinctionSection(BaseResultSection):
"""
The extinction (reddening) section in a DustResults object.
"""
def __init__(self, xml_root):
"""
Parameters
----------
xml_root : `xml.etree.ElementTree`
The xml tree containing the data for this section
"""
# Find the section's xml nodes
names = [DESC, DATA_IMAGE, DATA_TABLE, STATISTICS]
xml_nodes = self.node_dict(names, xml_root)
# Build the DustNodes
self._dust_nodes = [StringNode(xml_nodes[DESC], "ext desc", 100),
StringNode(xml_nodes[DATA_IMAGE], "ext image", 255),
StringNode(xml_nodes[DATA_TABLE], "ext table", 255)]
# Create statistics subsection
self._stats = StatsSection(xml_nodes[STATISTICS], "ext")
self.create_columns()
def create_columns(self):
"""Build the columns associated with this section."""
BaseResultSection.create_columns(self)
self._columns.extend(self._stats.columns)
@property
def table_url(self):
"""Return the url where the extinction detail table can be found."""
table_url = self._dust_nodes[2]._value
return table_url
@property
def image_url(self):
"""Return the url of the FITS image associated with this section."""
return self._dust_nodes[1]._value
def values(self):
"""Return the data values associated with this section,
i.e. the list of values corresponding to a single row in the results table."""
ext_values = BaseResultSection.values(self)
ext_values.extend(self._stats.values())
return ext_values
def __str__(self):
"""Return a string representation of the section."""
base_string = BaseResultSection.__str__(self)
string = "[ExtinctionSection: " + base_string + self._stats.__str__() + "]"
return string
class EmissionSection(BaseResultSection):
"""
The emission section in a DustResults object.
"""
def __init__(self, xml_root):
"""
Parameters
----------
xml_root : `xml.etree.ElementTree`
The xml tree containing the data for this section
"""
names = [DESC, DATA_IMAGE, STATISTICS]
xml_nodes = self.node_dict(names, xml_root)
# Create the DustNodes
self._dust_nodes = [StringNode(xml_nodes[DESC], "em desc", 100),
StringNode(xml_nodes[DATA_IMAGE], "em image", 255)]
# Create the statistics subsection
self._stats = StatsSection(xml_nodes[STATISTICS], "em")
self.create_columns()
def create_columns(self):
"""Build the columns associated with this section."""
BaseResultSection.create_columns(self)
self._columns.extend(self._stats.columns)
def values(self):
"""Return the data values associated with this section,
i.e. the list of values corresponding to a single row in the results table."""
values = BaseResultSection.values(self)
values.extend(self._stats.values())
return values
@property
def image_url(self):
"""Return the url of the FITS image associated with this section."""
return self._dust_nodes[1]._value
def __str__(self):
"""Return a string representation of the section."""
base_string = BaseResultSection.__str__(self)
string = "[EmissionSection: " + base_string + self._stats.__str__() + "]"
return string
class TemperatureSection(BaseResultSection):
"""
The temperature section in a DustResults object.
"""
def __init__(self, xml_root):
"""
Parameters
----------
xml_root : `xml.etree.ElementTree`
The xml tree containing the data for this section
"""
names = [DESC, DATA_IMAGE, STATISTICS]
xml_nodes = self.node_dict(names, xml_root)
# Create the DustNodes
self._dust_nodes = [StringNode(xml_nodes[DESC], "temp desc", 100),
StringNode(xml_nodes[DATA_IMAGE], "temp image", 255)]
# Create the statistics subsection
self._stats = StatsSection(xml_nodes[STATISTICS], "temp")
self.create_columns()
def create_columns(self):
"""Build the columns associated with this section."""
BaseResultSection.create_columns(self)
self._columns.extend(self._stats.columns)
def values(self):
"""Return the data values associated with this section,
i.e. the list of values corresponding to a single row in the results table."""
values = BaseResultSection.values(self)
values.extend(self._stats.values())
return values
@property
def image_url(self):
"""Return the url of the FITS image associated with this section."""
return self._dust_nodes[1]._value
def __str__(self):
"""Return a string representation of the section."""
base_string = BaseResultSection.__str__(self)
string = "[TemperatureSection: " + base_string + self._stats.__str__() + "]"
return string
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
Utilities used by irsa_dust.py to query the dust service and parse the results.
"""
import re
import xml.etree.ElementTree as tree
import astropy.units as u
import pdb
def parse_number(string):
"""
Retrieve a number from the string.
Parameters
----------
string : str
the string to parse
Returns
-------
number : float
the number contained in the string
"""
num_str = string.split(None, 1)[0]
number = float(num_str)
return number
def parse_coords(string):
"""
Retrieve coordinates from the string.
Parameters
----------
string : str
the string to parse
Returns
-------
coords : list(float, float, str)
list containing RA, Dec, and coordinate system description
"""
ra = float(string.split()[0])
dec = float(string.split()[1])
coord_sys = string.split(None, 2)[2].strip()
coords = [ra, dec, coord_sys]
return coords
def parse_units(string):
"""
Retrieve units from the string.
Parameters
----------
string: str
the string to parse
Returns
-------
units : `astropy.units.Unit`
the units contained in the string
"""
unit_str = string.split(None, 1)[1]
unit_str = re.sub("[()]", "", unit_str).strip()
units = u.format.Generic().parse(unit_str)
return units
def find_result_node(desc, xml_tree):
"""
Returns the <result> node with a <desc> child matching the given text.
Eg: if desc = "text to match", this function will find the following
result node:
<result>
<desc>text to match</desc>
</result>
Parameters
-----
xmlTree : the xml tree to search for the <result> node
desc : the text contained in the desc node
Returns
-----
node : the <result> node containing the child with the given desc
"""
result_nodes = xml_tree.findall("result")
for result_node in result_nodes:
result_desc = result_node.find("desc").text.strip()
if result_desc == desc:
return result_node
return None
def xml(response):
"""
Parse raw xml and return as an xml tree. If status is not `ok`, raise an exception.
Parameters
----------
response : str
unicode string containing xml
Returns
-------
xml_tree : `xml.etree.ElementTree`
an xml tree
"""
#get the root element from the xml string
root = tree.fromstring(response)
status = root.attrib["status"]
if status == "error":
message = root.find("message").text
raise Exception("The dust service responded with an error: " + message)
elif status != "ok":
raise Exception("Response status was not 'ok'.")
#construct the ElementTree from the root
xml_tree = tree.ElementTree(root)
return xml_tree
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment