( filterpath )
return specs, attr
>>> def parse_filter(path):
    ...     specs, attr =  _filterpath_to_node_specification(path)
    ...     return [(s.ns, s.tag, s.filter) for s in specs], attr
    ...
    >>> parse_filter('book[@author==Hopkins]/@barcode')
    ([('', 'book', '@author==Hopkins')], 'barcode')
    >>> parse_filter('books/*/title/author')
    ([('', 'books', ''), ('', '*', ''), ('', 'title', ''), ('', 'author', '')], None)
    >>> parse_filter('{ns}book[@price<20]/{ns}title')
    ([('{ns}', 'book', '@price<20'), ('{ns}', 'title', '')], None)
    >>> parse_filter('@id')
    ([], 'id')
    >>> parse_filter('@{ns}id')
    ([], '{ns}id')
    >>> parse_filter('{ns}@id')
    Traceback (most recent call last):
        ...
    InvalidFilterException
    >>> parse_filter('{ns}*')
    ([('{ns}', '*', '')], None)
    >>> parse_filter('{ns}*') == parse_filter('{ns}')
    True
    >>> parse_filter('{ns}/@id')
    ([('{ns}', '*', '')], 'id')
    >>> parse_filter('{ns}*[@name.startswith("A")]/@id')
    ([('{ns}', '*', '@name.startswith("A")')], 'id')
    >>> parse_filter('/portal/content-node[@create-type==explicit]/@action')
    ([('', 'portal', ''), ('', 'content-node', '@create-type==explicit')], 'action')
>>> def parse_filter(path):
    ...     specs, attr =  _filterpath_to_node_specification(path)
    ...     return [(s.ns, s.tag, s.filter) for s in specs], attr
    ...
    >>> parse_filter('book[
>>> def parse_filter ( path ) : ... specs attr = _filterpath_to_node_specification ( path ) ... return [ ( s . ns s . tag s . filter ) for s in specs ] attr ... >>> parse_filter ( book [
def _filterpath_to_node_specification( filterpath ):
    '''
    >>> def parse_filter(path):
    ...     specs, attr =  _filterpath_to_node_specification(path)
    ...     return [(s.ns, s.tag, s.filter) for s in specs], attr
    ...
    >>> parse_filter('book[@author==Hopkins]/@barcode')
    ([('', 'book', '@author==Hopkins')], 'barcode')
    >>> parse_filter('books/*/title/author')
    ([('', 'books', ''), ('', '*', ''), ('', 'title', ''), ('', 'author', '')], None)
    >>> parse_filter('{ns}book[@price<20]/{ns}title')
    ([('{ns}', 'book', '@price<20'), ('{ns}', 'title', '')], None)
    >>> parse_filter('@id')
    ([], 'id')
    >>> parse_filter('@{ns}id')
    ([], '{ns}id')
    >>> parse_filter('{ns}@id')
    Traceback (most recent call last):
        ...
    InvalidFilterException
    >>> parse_filter('{ns}*')
    ([('{ns}', '*', '')], None)
    >>> parse_filter('{ns}*') == parse_filter('{ns}')
    True
    >>> parse_filter('{ns}/@id')
    ([('{ns}', '*', '')], 'id')
    >>> parse_filter('{ns}*[@name.startswith("A")]/@id')
    ([('{ns}', '*', '@name.startswith("A")')], 'id')
    >>> parse_filter('/portal/content-node[@create-type==explicit]/@action')
    ([('', 'portal', ''), ('', 'content-node', '@create-type==explicit')], 'action')
    '''
    specs = []
    #does the filterpath end with an attribute, eg. @id ?
    #Note: RE_ATTRIBUTE will produce id, not @id
    attr = re.search(RE_ATTRIBUTE, filterpath)
    if attr:
        attr = attr.group(0)
        #remove the attribute just found from the filterpath
        filterpath =  re.sub(RE_ATTRIBUTE, '', filterpath)
        #strip trailing @
        filterpath = filterpath[:-1]
        if filterpath and filterpath[-1] != '/': raise InvalidFilterException()
    else:
        attr = None
    if filterpath:
        #the filterpath passed to RE_XPATH.findall() must begin with a forward slash
        if filterpath[0] != '/':
            filterpath = '/' + filterpath
        #strip any trailing forward slash
        if filterpath[-1] == '/':
            filterpath = filterpath[:-1]
    for ns, tag, filter in RE_XPATH.findall(filterpath):
        specs.append( _NodeSpecification(ns, tag, filter) )
    return specs, attr
def _filterpath_to_node_specification ( filterpath ) : specs = [ ] #does the filterpath end with an attribute, eg. @id ? #Note: RE_ATTRIBUTE will produce id, not @id attr = re . search ( RE_ATTRIBUTE , filterpath ) if attr : attr = attr . group ( 0 ) #remove the attribute just found from the filterpath filterpath = re . sub ( RE_ATTRIBUTE , '' , filterpath ) #strip trailing @ filterpath = filterpath [ : - 1 ] if filterpath and filterpath [ - 1 ] != '/' : raise InvalidFilterException ( ) else : attr = None if filterpath : #the filterpath passed to RE_XPATH.findall() must begin with a forward slash if filterpath [ 0 ] != '/' : filterpath = '/' + filterpath #strip any trailing forward slash if filterpath [ - 1 ] == '/' : filterpath = filterpath [ : - 1 ] for ns , tag , filter in RE_XPATH . findall ( filterpath ) : specs . append ( _NodeSpecification ( ns , tag , filter ) ) return specs , attr