# HG changeset patch # User athomas # Date 1181239382 0 # Node ID 9e11fa7f46038059e7ea6a0bd28bd5589ca38fa9 # Parent c1f2a859fd7523d18248c676651721c1eb88681a Cut and copy transformer operations can now operate on selected attributes. Also added an example of inserting content to the documentation. diff --git a/doc/filters.txt b/doc/filters.txt --- a/doc/filters.txt +++ b/doc/filters.txt @@ -164,12 +164,14 @@ ... ... ''') - >>> print html | Transformer('body//em').apply(unicode.upper, TEXT) \ - ... .unwrap().wrap(tag.u) + >>> print html | Transformer('body/em').apply(unicode.upper, TEXT) \ + ... .unwrap().wrap(tag.u).end() \ + ... .select('body/u') \ + ... .prepend('underlined ') Some Title - Some BODY text. + Some underlined BODY text. @@ -177,8 +179,9 @@ 1. matches any `` element anywhere in the body, 2. uppercases any text nodes in the element, - 3. strips off the `` start and close tags, and - 4. wraps the content in a `` tag. + 3. strips off the `` start and close tags, + 4. wraps the content in a `` tag, and + 5. inserts the text `underlind` inside the `` tag. A number of commonly useful transformations are available for this filter. Please consult the API documentation a complete list. diff --git a/genshi/filters/transform.py b/genshi/filters/transform.py --- a/genshi/filters/transform.py +++ b/genshi/filters/transform.py @@ -76,9 +76,8 @@ """Stream augmentation mark indicating that a match occurred outside a selected element.""" -ATTRIBUTE = TransformMark('ATTRIBUTE') -"""Stream augmentation mark indicating that a an element attribute was -selected.""" +ATTR = TransformMark('ATTR') +"""Stream augmentation mark indicating a selected element attribute.""" EXIT = TransformMark('EXIT') """Stream augmentation mark indicating that a selected element is being @@ -301,10 +300,6 @@ :rtype: `Transformer` """ - def _unwrap(stream): - for mark, event in stream: - if mark not in (ENTER, EXIT): - yield mark, event return self | UnwrapTransformation() def wrap(self, element): @@ -469,6 +464,20 @@ >>> print buffer body + Element attributes can also be copied for later use: + + >>> html = HTML('Some Title' + ... 'Some body' + ... 'text.') + >>> buffer = StreamBuffer() + >>> def apply_attr(name, entry): + ... return list(buffer)[0][1][1].get('class') + >>> print html | Transformer('body/em[@class]/@class').copy(buffer) \\ + ... .end().select('body/em[not(@class)]').attr('class', apply_attr) + Some TitleSome bodytext. + :param buffer: the `StreamBuffer` in which the selection should be stored @@ -543,7 +552,8 @@ def _unmark(self, stream): for mark, event in stream: - yield event + if event[0] is not None: + yield event class SelectTransformation(object): @@ -572,6 +582,8 @@ yield mark, event continue result = test(event, {}, {}) + # XXX This is effectively genshi.core._ensure() for transform + # streams. if result is True: if event[0] is START: yield ENTER, event @@ -589,9 +601,13 @@ test(subevent, {}, {}, updateonly=True) else: yield OUTSIDE, event + elif isinstance(result, Attrs): + # XXX Selected *attributes* are given a "kind" of None to + # indicate they are not really part of the stream. + yield ATTR, (None, (QName(event[1][0] + '@*'), result), event[2]) + yield None, event elif result: - yield None, event - yield ATTRIBUTE, result + yield None, (TEXT, unicode(result), (None, -1, -1)) else: yield None, event @@ -938,15 +954,41 @@ return stream -class CutTransformation(CopyTransformation, RemoveTransformation): +class CutTransformation(object): """Cut selected events into a buffer for later insertion and remove the selection. """ + def __init__(self, buffer): + """Create the cut transformation. + + :param buffer: the `StreamBuffer` in which the selection should be + stored + """ + self.buffer = buffer + def __call__(self, stream): """Apply the transform filter to the marked stream. :param stream: the marked event stream to filter """ - stream = CopyTransformation.__call__(self, stream) - return RemoveTransformation.__call__(self, stream) + out_stream = [] + attributes = None + for mark, (kind, data, pos) in stream: + if attributes: + assert kind is START + data = (data[0], data[1] - attributes) + attributes = None + if mark: + # There is some magic here. ATTR marked events are pushed into + # the stream *before* the START event they originated from. + # This allows cut() to strip out the attributes from START + # event as would be expected. + if mark is ATTR: + self.buffer.append((kind, data, pos)) + attributes = [name for name, _ in data[1]] + else: + self.buffer.append((kind, data, pos)) + else: + out_stream.append((mark, (kind, data, pos))) + return out_stream