# elementflow
Elementflow is a Python library for generating XML as a stream. Some existing
XML producing libraries (like ElementTree, lxml) build a whole XML tree in
memory and then serialize it. It might be inefficient for moderately large XML
payloads (think of a content-oriented Web service producing lots of XML data
output). Python's built-in xml.sax.saxutils.XMLGenerator is very low-level and
requires closing elements by hand.
Also, most XML libraries, to be honest, suck when dealing with namespaces.
## Usage
Basic XML generation:
```python
import elementflow
file = open('text.xml', 'w') # can be any object with .write() method
System Message: WARNING/2 (<string>, line 17); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 17); backlink
Inline interpreted text or phrase reference start-string without end-string.
- with elementflow.xml(file, u'root') as xml:
xml.element(u'item', attrs={u'key': u'value'}, text=u'text')
with xml.container(u'container', attrs={u'key': u'value'}):
System Message: ERROR/3 (<string>, line 24)
Unexpected indentation.
xml.text(u'text')
xml.element(u'subelement', text=u'subelement text')
System Message: WARNING/2 (<string>, line 26)
Definition list ends without a blank line; unexpected unindent.
```
System Message: WARNING/2 (<string>, line 26); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 26); backlink
Inline interpreted text or phrase reference start-string without end-string.
Using with is required to properly close container elements. The library
expects unicode strings on input and produces utf-8 encoded output (you may
omit those "u"s for pure ASCII strings if you want to, Python will convert
them automatically).
XML with namespaces:
```python
with elementflow.xml(file, 'root', namespaces={'': 'urn:n', 'n1': 'urn:n1'}) as xml:
System Message: WARNING/2 (<string>, line 35); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 35); backlink
Inline interpreted text or phrase reference start-string without end-string.
System Message: ERROR/3 (<string>, line 37)
Unexpected indentation.
xml.element('item')
with xml.container('container', namespaces={'n2': 'urn:n2'):
System Message: ERROR/3 (<string>, line 39)
Unexpected indentation.
xml.element('n1:subelement')
xml.element('n2:subelement')
System Message: WARNING/2 (<string>, line 41)
Block quote ends without a blank line; unexpected unindent.
```
System Message: WARNING/2 (<string>, line 41); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 41); backlink
Inline interpreted text or phrase reference start-string without end-string.
Elements with namespaces are defined using prefixes. You can define namespaces
at the root level and for any container. The library will check for namespace
prefixes that wasn't defined beforehand and will raise ValueError in that case.
Pretty-printing is also supported:
```python
with elementflow.xml(file, 'root', indent=True):
System Message: WARNING/2 (<string>, line 49); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 49); backlink
Inline interpreted text or phrase reference start-string without end-string.
System Message: ERROR/3 (<string>, line 51)
Unexpected indentation.
# ...
System Message: WARNING/2 (<string>, line 52)
Block quote ends without a blank line; unexpected unindent.
```
System Message: WARNING/2 (<string>, line 52); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 52); backlink
Inline interpreted text or phrase reference start-string without end-string.
In some cases it's more convenient to have such XML producer as a Python
iterator. This is easily done by wrapping XML generation code into a generator
function:
```python
def g():
System Message: WARNING/2 (<string>, line 58); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 58); backlink
Inline interpreted text or phrase reference start-string without end-string.
System Message: ERROR/3 (<string>, line 60)
Unexpected indentation.
xml = elementflow.xml(elementflow.Queue(), 'root')
with xml:
System Message: ERROR/3 (<string>, line 62)
Unexpected indentation.
- for item in collection:
- xml.element(...)
yield xml.file.pop()
System Message: WARNING/2 (<string>, line 65)
Block quote ends without a blank line; unexpected unindent.
yield xml.file.pop()
System Message: WARNING/2 (<string>, line 66)
Block quote ends without a blank line; unexpected unindent.
```
System Message: WARNING/2 (<string>, line 66); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 66); backlink
Inline interpreted text or phrase reference start-string without end-string.
elementflow.Queue() is a temporary buffer that accepts data from an XML
generator and is cleared upon calling .pop() on it. You also might want to
yield data from the iterator only when this buffer reaches a certain size:
```python
if len(xml.file) > BUFSIZE:
System Message: WARNING/2 (<string>, line 72); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 72); backlink
Inline interpreted text or phrase reference start-string without end-string.
System Message: ERROR/3 (<string>, line 74)
Unexpected indentation.
yield xml.file.pop()
System Message: WARNING/2 (<string>, line 75)
Block quote ends without a blank line; unexpected unindent.
```
System Message: WARNING/2 (<string>, line 75); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>, line 75); backlink
Inline interpreted text or phrase reference start-string without end-string.