Browse Source

HUE-3287 [core] Django 1.11 upgrade
- Adding et_xmlfile module

Prakash Ranade 7 years ago
parent
commit
4e31ff52b6

+ 0 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/MANIFEST.in


+ 35 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/PKG-INFO

@@ -0,0 +1,35 @@
+Metadata-Version: 1.1
+Name: et_xmlfile
+Version: 1.0.1
+Summary: An implementation of lxml.xmlfile for the standard library
+Home-page: https://bitbucket.org/openpyxl/et_xmlfile
+Author: See ATUHORS.txt
+Author-email: charlie.clark@clark-consulting.eu
+License: MIT
+Description: et_xmfile
+        =========
+        
+        et_xmlfile is a low memory library for creating large XML files.
+        
+        It is based upon the `xmlfile module from lxml <http://lxml.de/api.html#incremental-xml-generation>`_ with the aim of allowing code to be developed that will work with both libraries. It was developed initially for the openpyxl project but is now a standalone module.
+        
+        The code was written by Elias Rabel as part of the `Python Düsseldorf <http://pyddf.de>`_ openpyxl sprint in September 2014.
+        
+        
+        Note on performance
+        -------------------
+        
+        The code was not developed with performance in mind but turned out to be faster than the existing SAX-based implementation but is significantly slower than lxml's xmlfile. There is one area where an optimisation for lxml will negatively affect the performance of et_xmfile and that is when using the `.element()` method on an xmlfile context manager. It is, therefore, recommended not to use this, though the method is provided for code compatibility.
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: POSIX
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Requires: python (>=2.6.0)

+ 14 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/README.rst

@@ -0,0 +1,14 @@
+et_xmfile
+=========
+
+et_xmlfile is a low memory library for creating large XML files.
+
+It is based upon the `xmlfile module from lxml <http://lxml.de/api.html#incremental-xml-generation>`_ with the aim of allowing code to be developed that will work with both libraries. It was developed initially for the openpyxl project but is now a standalone module.
+
+The code was written by Elias Rabel as part of the `Python Düsseldorf <http://pyddf.de>`_ openpyxl sprint in September 2014.
+
+
+Note on performance
+-------------------
+
+The code was not developed with performance in mind but turned out to be faster than the existing SAX-based implementation but is significantly slower than lxml's xmlfile. There is one area where an optimisation for lxml will negatively affect the performance of et_xmfile and that is when using the `.element()` method on an xmlfile context manager. It is, therefore, recommended not to use this, though the method is provided for code compatibility.

+ 12 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/et_xmlfile/__init__.py

@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from .xmlfile import xmlfile
+
+# constants
+__version__ = '1.0.1'
+
+__author__ = 'See ATUHORS.txt'
+__license__ = 'MIT'
+__author_email__ = 'charlie.clark@clark-consulting.eu'
+__url__ = 'https://bitbucket.org/openpyxl/et_xmlfile'
+

+ 0 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/et_xmlfile/tests/__init__.py


+ 305 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/et_xmlfile/tests/common_imports.py

@@ -0,0 +1,305 @@
+import os
+import os.path
+import re
+import gc
+import sys
+import unittest
+
+try:
+    import urlparse
+except ImportError:
+    import urllib.parse as urlparse 
+
+try:
+    from urllib import pathname2url
+except:
+    from urllib.request import pathname2url
+
+
+def make_version_tuple(version_string):
+    l = []
+    for part in re.findall('([0-9]+|[^0-9.]+)', version_string):
+        try:
+            l.append(int(part))
+        except ValueError:
+            l.append(part)
+    return tuple(l)
+
+IS_PYPY = (getattr(sys, 'implementation', None) == 'pypy' or
+           getattr(sys, 'pypy_version_info', None) is not None)
+
+IS_PYTHON3 = sys.version_info[0] >= 3
+
+try:
+    from xml.etree import ElementTree # Python 2.5+
+except ImportError:
+    try:
+        from elementtree import ElementTree # standard ET
+    except ImportError:
+        ElementTree = None
+
+if hasattr(ElementTree, 'VERSION'):
+    ET_VERSION = make_version_tuple(ElementTree.VERSION)
+else:
+    ET_VERSION = (0,0,0)
+
+try:
+    from xml.etree import cElementTree # Python 2.5+
+except ImportError:
+    try:
+        import cElementTree # standard ET
+    except ImportError:
+        cElementTree = None
+
+if hasattr(cElementTree, 'VERSION'):
+    CET_VERSION = make_version_tuple(cElementTree.VERSION)
+else:
+    CET_VERSION = (0,0,0)
+
+def filter_by_version(test_class, version_dict, current_version):
+    """Remove test methods that do not work with the current lib version.
+    """
+    find_required_version = version_dict.get
+    def dummy_test_method(self):
+        pass
+    for name in dir(test_class):
+        expected_version = find_required_version(name, (0,0,0))
+        if expected_version > current_version:
+            setattr(test_class, name, dummy_test_method)
+
+try:
+    import doctest
+    # check if the system version has everything we need
+    doctest.DocFileSuite
+    doctest.DocTestParser
+    doctest.NORMALIZE_WHITESPACE
+    doctest.ELLIPSIS
+except (ImportError, AttributeError):
+    # we need our own version to make it work (Python 2.3?)
+    import local_doctest as doctest
+
+try:
+    sorted
+except NameError:
+    def sorted(seq, **kwargs):
+        seq = list(seq)
+        seq.sort(**kwargs)
+        return seq
+else:
+    locals()['sorted'] = sorted
+
+
+try:
+    next
+except NameError:
+    def next(it):
+        return it.next()
+else:
+    locals()['next'] = next
+
+
+try:
+    import pytest
+except ImportError:
+    class skipif(object):
+        "Using a class because a function would bind into a method when used in classes"
+        def __init__(self, *args): pass
+        def __call__(self, func, *args): return func
+else:
+    skipif = pytest.mark.skipif
+
+def _get_caller_relative_path(filename, frame_depth=2):
+    module = sys.modules[sys._getframe(frame_depth).f_globals['__name__']]
+    return os.path.normpath(os.path.join(
+            os.path.dirname(getattr(module, '__file__', '')), filename))
+
+from io import StringIO
+
+if sys.version_info[0] >= 3:
+    # Python 3
+    from builtins import str as unicode
+    def _str(s, encoding="UTF-8"):
+        return s
+    def _bytes(s, encoding="UTF-8"):
+        return s.encode(encoding)
+    from io import BytesIO as _BytesIO
+    def BytesIO(*args):
+        if args and isinstance(args[0], str):
+            args = (args[0].encode("UTF-8"),)
+        return _BytesIO(*args)
+
+    doctest_parser = doctest.DocTestParser()
+    _fix_unicode = re.compile(r'(\s+)u(["\'])').sub
+    _fix_exceptions = re.compile(r'(.*except [^(]*),\s*(.*:)').sub
+    def make_doctest(filename):
+        filename = _get_caller_relative_path(filename)
+        doctests = read_file(filename)
+        doctests = _fix_unicode(r'\1\2', doctests)
+        doctests = _fix_exceptions(r'\1 as \2', doctests)
+        return doctest.DocTestCase(
+            doctest_parser.get_doctest(
+                doctests, {}, os.path.basename(filename), filename, 0))
+else:
+    # Python 2
+    from __builtin__ import unicode
+    def _str(s, encoding="UTF-8"):
+        return unicode(s, encoding=encoding)
+    def _bytes(s, encoding="UTF-8"):
+        return s
+    from io import BytesIO
+
+    doctest_parser = doctest.DocTestParser()
+    _fix_traceback = re.compile(r'^(\s*)(?:\w+\.)+(\w*(?:Error|Exception|Invalid):)', re.M).sub
+    _fix_exceptions = re.compile(r'(.*except [^(]*)\s+as\s+(.*:)').sub
+    _fix_bytes = re.compile(r'(\s+)b(["\'])').sub
+    def make_doctest(filename):
+        filename = _get_caller_relative_path(filename)
+        doctests = read_file(filename)
+        doctests = _fix_traceback(r'\1\2', doctests)
+        doctests = _fix_exceptions(r'\1, \2', doctests)
+        doctests = _fix_bytes(r'\1\2', doctests)
+        return doctest.DocTestCase(
+            doctest_parser.get_doctest(
+                doctests, {}, os.path.basename(filename), filename, 0))
+
+try:
+    skipIf = unittest.skipIf
+except AttributeError:
+    def skipIf(condition, why,
+               _skip=lambda test_method: None,
+               _keep=lambda test_method: test_method):
+        if condition:
+            return _skip
+        return _keep
+
+
+class HelperTestCase(unittest.TestCase):
+    def tearDown(self):
+        gc.collect()
+
+    def parse(self, text, parser=None):
+        f = BytesIO(text) if isinstance(text, bytes) else StringIO(text)
+        return etree.parse(f, parser=parser)
+    
+    def _rootstring(self, tree):
+        return etree.tostring(tree.getroot()).replace(
+            _bytes(' '), _bytes('')).replace(_bytes('\n'), _bytes(''))
+
+    # assertFalse doesn't exist in Python 2.3
+    try:
+        unittest.TestCase.assertFalse
+    except AttributeError:
+        assertFalse = unittest.TestCase.failIf
+
+
+class SillyFileLike:
+    def __init__(self, xml_data=_bytes('<foo><bar/></foo>')):
+        self.xml_data = xml_data
+        
+    def read(self, amount=None):
+        if self.xml_data:
+            if amount:
+                data = self.xml_data[:amount]
+                self.xml_data = self.xml_data[amount:]
+            else:
+                data = self.xml_data
+                self.xml_data = _bytes('')
+            return data
+        return _bytes('')
+
+class LargeFileLike:
+    def __init__(self, charlen=100, depth=4, children=5):
+        self.data = BytesIO()
+        self.chars  = _bytes('a') * charlen
+        self.children = range(children)
+        self.more = self.iterelements(depth)
+
+    def iterelements(self, depth):
+        yield _bytes('<root>')
+        depth -= 1
+        if depth > 0:
+            for child in self.children:
+                for element in self.iterelements(depth):
+                    yield element
+                yield self.chars
+        else:
+            yield self.chars
+        yield _bytes('</root>')
+
+    def read(self, amount=None):
+        data = self.data
+        append = data.write
+        if amount:
+            for element in self.more:
+                append(element)
+                if data.tell() >= amount:
+                    break
+        else:
+            for element in self.more:
+                append(element)
+        result = data.getvalue()
+        data.seek(0)
+        data.truncate()
+        if amount:
+            append(result[amount:])
+            result = result[:amount]
+        return result
+
+class LargeFileLikeUnicode(LargeFileLike):
+    def __init__(self, charlen=100, depth=4, children=5):
+        LargeFileLike.__init__(self, charlen, depth, children)
+        self.data = StringIO()
+        self.chars  = _str('a') * charlen
+        self.more = self.iterelements(depth)
+
+    def iterelements(self, depth):
+        yield _str('<root>')
+        depth -= 1
+        if depth > 0:
+            for child in self.children:
+                for element in self.iterelements(depth):
+                    yield element
+                yield self.chars
+        else:
+            yield self.chars
+        yield _str('</root>')
+
+def fileInTestDir(name):
+    _testdir = os.path.dirname(__file__)
+    return os.path.join(_testdir, name)
+
+def path2url(path):
+    return urlparse.urljoin(
+        'file:', pathname2url(path))
+
+def fileUrlInTestDir(name):
+    return path2url(fileInTestDir(name))
+
+def read_file(name, mode='r'):
+    f = open(name, mode)
+    try:
+        data = f.read()
+    finally:
+        f.close()
+    return data
+
+def write_to_file(name, data, mode='w'):
+    f = open(name, mode)
+    try:
+        data = f.write(data)
+    finally:
+        f.close()
+
+def readFileInTestDir(name, mode='r'):
+    return read_file(fileInTestDir(name), mode)
+
+def canonicalize(xml):
+    tree = etree.parse(BytesIO(xml) if isinstance(xml, bytes) else StringIO(xml))
+    f = BytesIO()
+    tree.write_c14n(f)
+    return f.getvalue()
+
+def unentitify(xml):
+    for entity_name, value in re.findall("(&#([0-9]+);)", xml):
+        xml = xml.replace(entity_name, unichr(int(value)))
+    return xml

+ 21 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/et_xmlfile/tests/helper.py

@@ -0,0 +1,21 @@
+from __future__ import absolute_import
+# Copyright (c) 2010-2015 openpyxl
+
+# Python stdlib imports
+from lxml.doctestcompare import LXMLOutputChecker, PARSE_XML
+
+
+def compare_xml(generated, expected):
+    """Use doctest checking from lxml for comparing XML trees. Returns diff if the two are not the same"""
+    checker = LXMLOutputChecker()
+
+    class DummyDocTest():
+        pass
+
+    ob = DummyDocTest()
+    ob.want = expected
+
+    check = checker.check_output(expected, generated, PARSE_XML)
+    if check is False:
+        diff = checker.output_difference(ob, generated, PARSE_XML)
+        return diff

+ 356 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/et_xmlfile/tests/test_incremental_xmlfile.py

@@ -0,0 +1,356 @@
+from __future__ import absolute_import
+
+"""
+Tests for the incremental XML serialisation API.
+
+Adapted from the tests from lxml.etree.xmlfile
+"""
+
+try:
+    import lxml
+except ImportError:
+    raise ImportError("lxml is required to run the tests.")
+
+
+from io import BytesIO
+import unittest
+import tempfile, os, sys
+
+from .common_imports import HelperTestCase, skipIf
+from et_xmlfile import xmlfile
+from et_xmlfile.xmlfile import LxmlSyntaxError
+
+import pytest
+from .helper import compare_xml
+
+import xml.etree.ElementTree
+from xml.etree.ElementTree import Element, parse
+
+
+class _XmlFileTestCaseBase(HelperTestCase):
+    _file = None  # to be set by specific subtypes below
+
+    def setUp(self):
+        self._file = BytesIO()
+
+    def test_element(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                pass
+        self.assertXml('<test></test>')
+
+    def test_element_write_text(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                xf.write('toast')
+        self.assertXml('<test>toast</test>')
+
+    def test_element_nested(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                with xf.element('toast'):
+                    with xf.element('taste'):
+                        xf.write('conTent')
+        self.assertXml('<test><toast><taste>conTent</taste></toast></test>')
+
+    def test_element_nested_with_text(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                xf.write('con')
+                with xf.element('toast'):
+                    xf.write('tent')
+                    with xf.element('taste'):
+                        xf.write('inside')
+                    xf.write('tnet')
+                xf.write('noc')
+        self.assertXml('<test>con<toast>tent<taste>inside</taste>'
+                       'tnet</toast>noc</test>')
+
+    def test_write_Element(self):
+        with xmlfile(self._file) as xf:
+            xf.write(Element('test'))
+        self.assertXml('<test/>')
+
+    def test_write_Element_repeatedly(self):
+        element = Element('test')
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                for i in range(100):
+                    xf.write(element)
+
+        tree = self._parse_file()
+        self.assertTrue(tree is not None)
+        self.assertEqual(100, len(tree.getroot()))
+        self.assertEqual(set(['test']), set(el.tag for el in tree.getroot()))
+
+    def test_namespace_nsmap(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('{nsURI}test', nsmap={'x': 'nsURI'}):
+                pass
+        self.assertXml('<x:test xmlns:x="nsURI"></x:test>')
+
+    def test_namespace_nested_nsmap(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test', nsmap={'x': 'nsURI'}):
+                with xf.element('{nsURI}toast'):
+                    pass
+        self.assertXml('<test xmlns:x="nsURI"><x:toast></x:toast></test>')
+
+    def test_anonymous_namespace(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('{nsURI}test'):
+                pass
+        self.assertXml('<ns0:test xmlns:ns0="nsURI"></ns0:test>')
+
+    def test_namespace_nested_anonymous(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                with xf.element('{nsURI}toast'):
+                    pass
+        self.assertXml('<test><ns0:toast xmlns:ns0="nsURI"></ns0:toast></test>')
+
+    def test_default_namespace(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('{nsURI}test', nsmap={None: 'nsURI'}):
+                pass
+        self.assertXml('<test xmlns="nsURI"></test>')
+
+    def test_nested_default_namespace(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('{nsURI}test', nsmap={None: 'nsURI'}):
+                with xf.element('{nsURI}toast'):
+                    pass
+        self.assertXml('<test xmlns="nsURI"><toast></toast></test>')
+
+    @pytest.mark.xfail
+    def test_pi(self):
+        from et_xmlfile.xmlfile import ProcessingInstruction
+        with xmlfile(self._file) as xf:
+            xf.write(ProcessingInstruction('pypi'))
+            with xf.element('test'):
+                pass
+        self.assertXml('<?pypi ?><test></test>')
+
+    @pytest.mark.xfail
+    def test_comment(self):
+        with xmlfile(self._file) as xf:
+            xf.write(etree.Comment('a comment'))
+            with xf.element('test'):
+                pass
+        self.assertXml('<!--a comment--><test></test>')
+
+    def test_attribute(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test', attrib={'k': 'v'}):
+                pass
+        self.assertXml('<test k="v"></test>')
+
+    def test_escaping(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                xf.write('Comments: <!-- text -->\n')
+                xf.write('Entities: &amp;')
+        self.assertXml(
+            '<test>Comments: &lt;!-- text --&gt;\nEntities: &amp;amp;</test>')
+
+    @pytest.mark.xfail
+    def test_encoding(self):
+        with xmlfile(self._file, encoding='utf16') as xf:
+            with xf.element('test'):
+                xf.write('toast')
+        self.assertXml('<test>toast</test>', encoding='utf16')
+
+    @pytest.mark.xfail
+    def test_buffering(self):
+        with xmlfile(self._file, buffered=False) as xf:
+            with xf.element('test'):
+                self.assertXml("<test>")
+                xf.write('toast')
+                self.assertXml("<test>toast")
+                with xf.element('taste'):
+                    self.assertXml("<test>toast<taste>")
+                    xf.write('some', etree.Element("more"), "toast")
+                    self.assertXml("<test>toast<taste>some<more/>toast")
+                self.assertXml("<test>toast<taste>some<more/>toast</taste>")
+                xf.write('end')
+                self.assertXml("<test>toast<taste>some<more/>toast</taste>end")
+            self.assertXml("<test>toast<taste>some<more/>toast</taste>end</test>")
+        self.assertXml("<test>toast<taste>some<more/>toast</taste>end</test>")
+
+    @pytest.mark.xfail
+    def test_flush(self):
+        with xmlfile(self._file, buffered=True) as xf:
+            with xf.element('test'):
+                self.assertXml("")
+                xf.write('toast')
+                self.assertXml("")
+                with xf.element('taste'):
+                    self.assertXml("")
+                    xf.flush()
+                    self.assertXml("<test>toast<taste>")
+                self.assertXml("<test>toast<taste>")
+            self.assertXml("<test>toast<taste>")
+        self.assertXml("<test>toast<taste></taste></test>")
+
+    def test_failure_preceding_text(self):
+        try:
+            with xmlfile(self._file) as xf:
+                xf.write('toast')
+        except LxmlSyntaxError:
+            self.assertTrue(True)
+        else:
+            self.assertTrue(False)
+
+    def test_failure_trailing_text(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                pass
+            try:
+                xf.write('toast')
+            except LxmlSyntaxError:
+                self.assertTrue(True)
+            else:
+                self.assertTrue(False)
+
+    def test_failure_trailing_Element(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                pass
+            try:
+                xf.write(Element('test'))
+            except LxmlSyntaxError:
+                self.assertTrue(True)
+            else:
+                self.assertTrue(False)
+
+    @pytest.mark.xfail
+    def test_closing_out_of_order_in_error_case(self):
+        cm_exit = None
+        try:
+            with xmlfile(self._file) as xf:
+                x = xf.element('test')
+                cm_exit = x.__exit__
+                x.__enter__()
+                raise ValueError('123')
+        except ValueError:
+            self.assertTrue(cm_exit)
+            try:
+                cm_exit(ValueError, ValueError("huhu"), None)
+            except LxmlSyntaxError:
+                self.assertTrue(True)
+            else:
+                self.assertTrue(False)
+        else:
+            self.assertTrue(False)
+
+    def _read_file(self):
+        pos = self._file.tell()
+        self._file.seek(0)
+        try:
+            return self._file.read()
+        finally:
+            self._file.seek(pos)
+
+    def _parse_file(self):
+        pos = self._file.tell()
+        self._file.seek(0)
+        try:
+            return parse(self._file)
+        finally:
+            self._file.seek(pos)
+
+    def tearDown(self):
+        if self._file is not None:
+            self._file.close()
+
+    def assertXml(self, expected, encoding='utf8'):
+        diff = compare_xml(self._read_file().decode(encoding), expected)
+        assert diff is None, diff
+
+
+class BytesIOXmlFileTestCase(_XmlFileTestCaseBase):
+    def setUp(self):
+        self._file = BytesIO()
+
+    def test_filelike_close(self):
+        with xmlfile(self._file, close=True) as xf:
+            with xf.element('test'):
+                pass
+        self.assertRaises(ValueError, self._file.getvalue)
+
+
+class TempXmlFileTestCase(_XmlFileTestCaseBase):
+    def setUp(self):
+        self._file = tempfile.TemporaryFile()
+
+
+class TempPathXmlFileTestCase(_XmlFileTestCaseBase):
+    def setUp(self):
+        self._tmpfile = tempfile.NamedTemporaryFile(delete=False)
+        self._file = self._tmpfile.name
+
+    def tearDown(self):
+        try:
+            self._tmpfile.close()
+        finally:
+            if os.path.exists(self._tmpfile.name):
+                os.unlink(self._tmpfile.name)
+
+    def _read_file(self):
+        self._tmpfile.seek(0)
+        return self._tmpfile.read()
+
+    def _parse_file(self):
+        self._tmpfile.seek(0)
+        return parse(self._tmpfile)
+
+    @skipIf(True, "temp file behaviour is too platform specific here")
+    def test_buffering(self):
+        pass
+
+    @skipIf(True, "temp file behaviour is too platform specific here")
+    def test_flush(self):
+        pass
+
+
+class SimpleFileLikeXmlFileTestCase(_XmlFileTestCaseBase):
+    class SimpleFileLike(object):
+        def __init__(self, target):
+            self._target = target
+            self.write = target.write
+            self.tell = target.tell
+            self.seek = target.seek
+            self.closed = False
+
+        def close(self):
+            assert not self.closed
+            self.closed = True
+            self._target.close()
+
+    def setUp(self):
+        self._target = BytesIO()
+        self._file = self.SimpleFileLike(self._target)
+
+    def _read_file(self):
+        return self._target.getvalue()
+
+    def _parse_file(self):
+        pos = self._file.tell()
+        self._target.seek(0)
+        try:
+            return parse(self._target)
+        finally:
+            self._target.seek(pos)
+
+    def test_filelike_not_closing(self):
+        with xmlfile(self._file) as xf:
+            with xf.element('test'):
+                pass
+        self.assertFalse(self._file.closed)
+
+    def test_filelike_close(self):
+        with xmlfile(self._file, close=True) as xf:
+            with xf.element('test'):
+                pass
+        self.assertTrue(self._file.closed)
+        self._file = None  # prevent closing in tearDown()

+ 104 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/et_xmlfile/xmlfile.py

@@ -0,0 +1,104 @@
+from __future__ import absolute_import
+# Copyright (c) 2010-2015 openpyxl
+
+"""Implements the lxml.etree.xmlfile API using the standard library xml.etree"""
+
+
+from contextlib import contextmanager
+
+from xml.etree.ElementTree import Element, tostring
+
+
+class LxmlSyntaxError(Exception):
+    pass
+
+
+class _FakeIncrementalFileWriter(object):
+    """Replacement for _IncrementalFileWriter of lxml.
+       Uses ElementTree to build xml in memory."""
+    def __init__(self, output_file):
+        self._element_stack = []
+        self._top_element = None
+        self._file = output_file
+        self._have_root = False
+
+    @contextmanager
+    def element(self, tag, attrib=None, nsmap=None, **_extra):
+        """Create a new xml element using a context manager.
+        The elements are written when the top level context is left.
+
+        This is for code compatibility only as it is quite slow.
+        """
+
+        # __enter__ part
+        self._have_root = True
+        if attrib is None:
+            attrib = {}
+        self._top_element = Element(tag, attrib=attrib, **_extra)
+        self._top_element.text = ''
+        self._top_element.tail = ''
+        self._element_stack.append(self._top_element)
+        yield
+
+        # __exit__ part
+        el = self._element_stack.pop()
+        if self._element_stack:
+            parent = self._element_stack[-1]
+            parent.append(self._top_element)
+            self._top_element = parent
+        else:
+            self._write_element(el)
+            self._top_element = None
+
+    def write(self, arg):
+        """Write a string or subelement."""
+
+        if isinstance(arg, str):
+            # it is not allowed to write a string outside of an element
+            if self._top_element is None:
+                raise LxmlSyntaxError()
+
+            if len(self._top_element) == 0:
+                # element has no children: add string to text
+                self._top_element.text += arg
+            else:
+                # element has children: add string to tail of last child
+                self._top_element[-1].tail += arg
+
+        else:
+            if self._top_element is not None:
+                self._top_element.append(arg)
+            elif not self._have_root:
+                self._write_element(arg)
+            else:
+                raise LxmlSyntaxError()
+
+    def _write_element(self, element):
+        xml = tostring(element)
+        self._file.write(xml)
+
+    def __enter__(self):
+        pass
+
+    def __exit__(self, type, value, traceback):
+        # without root the xml document is incomplete
+        if not self._have_root:
+            raise LxmlSyntaxError()
+
+
+class xmlfile(object):
+    """Context manager that can replace lxml.etree.xmlfile."""
+    def __init__(self, output_file, buffered=False, encoding=None, close=False):
+        if isinstance(output_file, str):
+            self._file = open(output_file, 'wb')
+            self._close = True
+        else:
+            self._file = output_file
+            self._close = close
+
+    def __enter__(self):
+        return _FakeIncrementalFileWriter(self._file)
+
+    def __exit__(self, type, value, traceback):
+        if self._close == True:
+            self._file.close()

+ 5 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/setup.cfg

@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+

+ 74 - 0
desktop/core/ext-py/et_xmlfile-1.0.1/setup.py

@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+
+"""Setup script for packaging et_xmfile.
+
+Requires setuptools.
+
+To build the setuptools egg use
+    python setup.py bdist_egg
+and either upload it to the PyPI with:
+    python setup.py upload
+or upload to your own server and register the release with PyPI:
+    python setup.py register
+
+A source distribution (.zip) can be built with
+    python setup.py sdist --format=zip
+
+That uses the manifest.in file for data files rather than searching for
+them here.
+
+"""
+import codecs
+import sys
+import os
+import warnings
+if sys.version_info < (2, 6):
+    raise Exception("Python >= 2.6 is required.")
+elif sys.version_info[:2] == (3, 2):
+    warnings.warn("Python 3.2 is no longer officially supported")
+
+from setuptools import setup, Extension, find_packages
+import re
+
+here = os.path.abspath(os.path.dirname(__file__))
+try:
+    with codecs.open(os.path.join(here, 'README.rst'), encoding="utf-8") as f:
+        README = f.read()
+except IOError:
+    README = ''
+
+from et_xmlfile import (
+    __author__,
+    __license__,
+    __author_email__,
+    __url__,
+    __version__
+)
+
+
+setup(name='et_xmlfile',
+    packages=find_packages(),
+    # metadata
+    version=__version__,
+    description="An implementation of lxml.xmlfile for the standard library",
+    long_description=README,
+    author=__author__,
+    author_email=__author_email__,
+    url=__url__,
+    license=__license__,
+    requires=[
+        'python (>=2.6.0)',
+        ],
+    classifiers=[
+                 'Development Status :: 5 - Production/Stable',
+                 'Operating System :: MacOS :: MacOS X',
+                 'Operating System :: Microsoft :: Windows',
+                 'Operating System :: POSIX',
+                 'License :: OSI Approved :: MIT License',
+                 'Programming Language :: Python',
+                 'Programming Language :: Python :: 2.6',
+                 'Programming Language :: Python :: 2.7',
+                 'Programming Language :: Python :: 3.3',
+                 'Programming Language :: Python :: 3.4',
+                 ],
+    )