Edgewall Software

Ticket #102: xpointer_xpath.diff

File xpointer_xpath.diff, 6.2 KB (added by nicoe@…, 4 years ago)

A less naïve implementation (including xpointer xpath support)

  • util.py

     
    243243    :return: the text with tags removed 
    244244    """ 
    245245    return _STRIPTAGS_RE.sub('', text) 
     246 
     247def xpointer2xpath(xpointer): 
     248    """Returns the XPath representation of an XPointer attribute. 
     249 
     250    >>> xpointer2xpath(None) 
     251    '//*' 
     252 
     253    We should return "id('id1')" but the xpath implementation of genshi does 
     254    not support that. 
     255 
     256    >>> xpointer2xpath('id1') 
     257    "//node()[@id='id1']" 
     258    >>> xpointer2xpath('id1/2') 
     259    "//node()[@id='id1']/*[2]" 
     260 
     261    Absolute XPointer expression should also be supported although the current 
     262    XPath parser used by genshi does not support them. 
     263     
     264    >>> xpointer2xpath('/1/2/3') 
     265    '/*[1]/*[2]/*[3]' 
     266 
     267    Support for XPath in the xpointer scheme 
     268 
     269    >>> xpointer2xpath('xpointer("//tag")') 
     270    '//tag' 
     271    """ 
     272    if xpointer is None: 
     273        return '//*' 
     274    match = re.match(r'''xpointer\(["'](.*)["']\)''', xpointer) 
     275    if match: 
     276        return match.groups()[0] 
     277 
     278    path = xpointer.split('/') 
     279    start = path.pop(0) 
     280    if start == '': 
     281        xpath = [''] 
     282    else: 
     283        xpath = ["//node()[@id='%s']" % start] 
     284    for segment in path: 
     285        xpath.append('*[%s]' % segment) 
     286    return '/'.join(xpath) 
  • template/base.py

     
    2424import sys 
    2525 
    2626from genshi.core import Attrs, Stream, StreamEventKind, START, TEXT, _ensure 
    27 from genshi.input import ParseError 
     27from genshi.input import ParseError, XML 
     28from genshi.util import xpointer2xpath 
    2829 
    2930__all__ = ['Context', 'DirectiveFactory', 'Template', 'TemplateError', 
    3031           'TemplateRuntimeError', 'TemplateSyntaxError', 'BadDirectiveError'] 
     
    467468                        yield event 
    468469            else: 
    469470                if kind is INCLUDE: 
    470                     href, cls, fallback = data 
     471                    href, xpointer, cls, fallback = data 
    471472                    if isinstance(href, basestring) and \ 
    472473                            not getattr(self.loader, 'auto_reload', True): 
    473474                        # If the path to the included template is static, and 
     
    476477                        try: 
    477478                            tmpl = self.loader.load(href, relative_to=pos[0], 
    478479                                                    cls=cls or self.__class__) 
    479                             for event in tmpl.stream: 
     480                            xpath = xpointer2xpath(xpointer) 
     481                            stream = Stream(tmpl.stream) 
     482                            for event in stream.select(xpath): 
    480483                                yield event 
    481484                        except TemplateNotFound: 
    482485                            if fallback is None: 
     
    486489                        continue 
    487490                    elif fallback: 
    488491                        # Otherwise the include is performed at run time 
    489                         data = href, cls, list(self._prepare(fallback)) 
     492                        data = (href, xpointer, cls,  
     493                                list(self._prepare(fallback))) 
    490494 
    491495                yield kind, data, pos 
    492496 
     
    602606 
    603607        for event in stream: 
    604608            if event[0] is INCLUDE: 
    605                 href, cls, fallback = event[1] 
     609                href, xpointer, cls, fallback = event[1] 
    606610                if not isinstance(href, basestring): 
    607611                    parts = [] 
    608612                    for subkind, subdata, subpos in self._eval(href, ctxt, 
     
    613617                try: 
    614618                    tmpl = self.loader.load(href, relative_to=event[2][0], 
    615619                                            cls=cls or self.__class__) 
    616                     for event in tmpl.generate(ctxt, **vars): 
     620                    xpath = xpointer2xpath(xpointer) 
     621                    stream = XML(tmpl.generate(ctxt, **vars)).select(xpath) 
     622                    for event in stream: 
    617623                        yield event 
    618624                except TemplateNotFound: 
    619625                    if fallback is None: 
  • template/markup.py

     
    222222                            raise TemplateSyntaxError('Include misses required ' 
    223223                                                      'attribute "href"', 
    224224                                                      self.filepath, *pos[1:]) 
    225                         includes.append((include_href, attrs.get('parse'))) 
     225                        if attrs.get('parse') == 'text' and \ 
     226                           attrs.get('xpointer') is not None: 
     227                            raise TemplateSyntaxError('Include use an xpointer ' 
     228                                                      'while parsing a text', 
     229                                                      self.filepath, *pos[1:]) 
     230                        includes.append((include_href, attrs.get('parse'), 
     231                                         attrs.get('xpointer'))) 
    226232                        streams.append([]) 
    227233                    elif tag.localname == 'fallback': 
    228234                        streams.append([]) 
     
    240246                    streams.pop() # discard anything between the include tags 
    241247                                  # and the fallback element 
    242248                    stream = streams[-1] 
    243                     href, parse = includes.pop() 
     249                    href, parse, xpointer = includes.pop() 
    244250                    try: 
    245251                        cls = { 
    246252                            'xml': MarkupTemplate, 
     
    250256                        raise TemplateSyntaxError('Invalid value for "parse" ' 
    251257                                                  'attribute of include', 
    252258                                                  self.filepath, *pos[1:]) 
    253                     stream.append((INCLUDE, (href, cls, fallback), pos)) 
     259                    stream.append((INCLUDE, (href, xpointer, cls, fallback), 
     260                                   pos)) 
    254261                else: 
    255262                    stream.append((kind, data, pos)) 
    256263