class Field(object):
"""Class which contains logic of field-processing
"""
def __init__(self, routes, callback=None, is_iterable=False,default=None):
"""
Arguments:
- `routes`: sequence of xPath routes to the field, in order of descending priority.
- `callback`: callable handler of field
- `is_iterable`: if there may be a more then one field with a such route in the xml Document
"""
self._routes = routes
self._callback = callback
self._is_iterable = is_iterable
self._default = default
def _clean_up(self, r):
"""Cleans up the result of xpath in according to
self._is_iterable value and some parameters qualiteies.
Sometimes xpath method returns list containing one element, but field is not iterable, so we need not the list, but it's single element
Arguments:
- `r`: conversable to True result of lxml.etree._Element.xpath method.
"""
if isinstance(r,list) and self._is_iterable:
return map(self._callback,r) if callable(self._callback) else r
if isinstance(r,list) and not self._is_iterable:
if len(r) == 1:
return self._callback(r.pop()) if callable(self._callback) else r.pop()
else:
raise error.RuntimeError('Many instances of non-iterable field')
else:
return self._callback(r) if callable(self._callback) else r
def get_value(self,xml,nsmap = namespaces):
"""Returns value of the field in passed document
If you passed False value of is_iterable to construct, you get a SINGLE result or error, not a list
if you passed True, you get LIST, which may contain one element.
if the field was not found, you get None. Anyway.
Arguments:
- `xml`: lxml.etree._Element instance
- `nsmap`: dict with namespaces for xpath.
"""
if not etree.iselement(xml):
raise error.InvalidArgumentError('Passed document is not valid lxml.etree Element');
for route in self._routes:
if xml.xpath(route):
return self._clean_up(xml.xpath(route,namespaces=nsmap))
else:
return self._default