Edgewall Software

Opened 12 years ago

Last modified 7 years ago

#521 new defect

genshi can't select xpath element based on its contents

Reported by: Chad Phillips <chad@…> Owned by: cmlenz
Priority: major Milestone: 0.9
Component: General Version: 0.6
Keywords: Cc: chad@…, leho@…

Description (last modified by hodgestar)

While customizing my Trac installation, I ran across a particularly sticky problem: it does not seem possible to use the contains() xpath function in genshi in a way that allows conditionally selecting an element based on its contents.

The particular example are labels in the change history for Trac. These are inside strong tags, but have no tag metadata to use for the xpath selector, so matching on the contents of the strong tag is the only way to alter the ones I want.

The non-working code that I believe should work:

      <!--
       Rewrite labels in the change history.
       This is busted but *should* work
       The error I get is:
              File "/usr/lib/python2.6/site-packages/Genshi-0.6-py2.6.egg/genshi/path.py",
             line 932, in as_float
             return float(as_scalar(value))
             ValueError: invalid literal for float(): .
     -->
      <strong py:match="ul[@class='changes']/li/strong[contains(.,'Reporter')]"
          py:attrs="select('@*')">
        Champion:
      </strong>

Also tried text() instead of . in contains(), that also errors out.

Here is the code I finally got to work, after literally hours of screwing around and research:

     <!--
       Rewrite labels in the change history.
     -->
      <strong py:match="ul[@class='changes']/li/strong"
          py:attrs="select('@*')">
        <py:with vars="text=select('text()').render();">
          <py:choose test="text">
            <py:when test="'Owner'">Liaison</py:when>
            <py:when test="'Reporter'">Champion</py:when>
            <py:when test="'Milestone'">Target</py:when>
            <py:otherwise>$text</py:otherwise>
          </py:choose>
        </py:with>
      </strong>

If I hadn't eventually realized that select() is represented as a stream instead of a string, I never would have gotten this working, either. Thankfully the documentation at http://genshi.edgewall.org/wiki/Documentation/0.5.x/streams.html#serialization provided me with the render() method so I could finally compare strings in py:choose.

I'd like to strongly advocate that contains() be fixed to work properly, as it shouldn't be necessary to jump through all these hoops in order to match on an element's contents.

Change History (4)

comment:1 Changed 12 years ago by Chad Phillips <chad@…>

  • Cc chad@… added

comment:2 Changed 12 years ago by hodgestar

  • Description modified (diff)

comment:3 Changed 10 years ago by lkraav <leho@…>

  • Cc leho@… added

comment:4 Changed 7 years ago by hodgestar

  • Milestone changed from 0.7 to 0.9

Moved to milestone 0.9.

Note: See TracTickets for help on using tickets.