* Style cleanup
* Updated comments/docstrings
* Renamed class XMLBase -> XMLTreeFile for clarity
* Merged XMLTreeFile.readablefile() into __init__()
* Made XMLTreeFile.sourcebackupfile content uniform
* Made XMLTreeFile.sourcefilename content uniform
* Updated & enhanced unittests

Signed-off-by: Chris Evich <[email protected]>
---
 client/shared/xml_utils.py          |   86 ++++++++++++++++++++++++-----------
 client/shared/xml_utils_unittest.py |   38 +++++++++++++--
 2 files changed, 92 insertions(+), 32 deletions(-)

diff --git a/client/shared/xml_utils.py b/client/shared/xml_utils.py
index 6f90aa4..4d5c977 100644
--- a/client/shared/xml_utils.py
+++ b/client/shared/xml_utils.py
@@ -20,7 +20,7 @@ TMPSFX='.xml'
 
 class TempXMLFile(file):
     """
-    Temporary XML file removed on instance deletion / unexceptional module 
exit.
+    Temporary XML file auto-removed on instance del / module exit.
     """
 
     def __init__(self, suffix=TMPSFX, prefix=TMPPFX, mode="wb+", buffer=1):
@@ -36,77 +36,99 @@ class TempXMLFile(file):
         os.close(fd)
         super(TempXMLFile, self).__init__(path, mode, buffer)
 
+
     def __exit__(self, exc_type, exc_value, traceback):
         """
         Always remove temporary file on module exit.
         """
+
         self.__del__()
         super(TempXMLFile, self).__exit__(exc_type, exc_value, traceback)
 
+
     def __del__(self):
         """
         Remove temporary file on instance delete.
         """
+
         try:
             os.unlink(self.name)
         except OSError:
             pass # don't care
 
+
 class XMLBackup(TempXMLFile):
-    """Temporary XML backuap, removed on unexceptional destruction."""
+    """
+    Backup file copy of XML data, automatically removed on instance 
destruction.
+    """
 
+    # Allow users to reference original source of XML data
     sourcefilename = None
 
     def __init__(self, sourcefilename):
         """
         Initialize a temporary backup from sourcefilename.
         """
+
         super(XMLBackup, self).__init__()
         self.sourcefilename = sourcefilename
         self.backup()
 
+
     def backup(self):
         """
         Overwrite temporary backup with contents of original source.
         """
+
         self.flush()
         self.seek(0)
         shutil.copyfileobj(file(self.sourcefilename, "rb"), 
super(XMLBackup,self))
         self.seek(0)
 
+
     def restore(self):
         """
         Overwrite original source with contents of temporary backup
         """
+
         self.flush()
         self.seek(0)
         shutil.copyfileobj(super(XMLBackup,self), file(self.sourcefilename, 
"wb+"))
         self.seek(0)
 
+
     def _info(self):
         logging.info("Retaining backup of %s in %s", self.sourcefilename,
                                                      self.name)
 
+
     def __exit__(self, exc_type, exc_value, traceback):
         """
         Remove temporary backup on unexceptional module exit.
         """
+
         if exc_type is None and exc_value is None and traceback is None:
             super(XMLBackup, self).__del__()
         else:
             self._info()
 
+
     def __del__(self):
         """
         Remove temporary file on instance delete.
         """
+
         self._info()
 
-class XMLBase(ElementTree.ElementTree, XMLBackup):
-    """ElementTree backed by a file copy of source"""
 
-    # Automaticaly remove temp file instance destruction
-    tempsource = None
+class XMLTreeFile(ElementTree.ElementTree, XMLBackup):
+    """
+    Combination of ElementTree root and auto-cleaned XML backup file.
+    """
+
+    # Closed file object of original source or TempXMLFile
+    # self.sourcefilename inherited from parent
+    sourcebackupfile = None
 
     def __init__(self, xml):
         """
@@ -114,57 +136,60 @@ class XMLBase(ElementTree.ElementTree, XMLBackup):
 
         param: xml: A filename or string containing XML
         """
+
         # xml param could be xml string or readable filename
-        if not self.readablefile(xml):
-            self.tempsource = TempXMLFile()
-            self.tempsource.write(xml)
+        # If it's a string, use auto-delete TempXMLFile
+        # to hold the original content.
+        try:
+            self.sourcebackupfile = open(xml, "rb")
             # Prevent source modification
-            self.tempsource.close()
-            xml = self.tempsource.name
-        # xml guaranteed to be a filename
+            self.sourcebackupfile.close()
+        except (IOError, OSError):
+            # this will auto-delete when XMLBase instance goes out of scope
+            self.sourcebackupfile = TempXMLFile()
+            self.sourcebackupfile.write(xml)
+            # Prevent source modification
+            self.sourcebackupfile.close()
+            xml = self.sourcebackupfile.name
         XMLBackup.__init__(self, sourcefilename=xml)
-        ElementTree.ElementTree.__init__(self, element=None, file=xml)
+        ElementTree.ElementTree.__init__(self, element=None,
+                                         file=self.name)
         # Ensure parsed content matches file content
         self.write()
         self.flush()
 
-    @classmethod
-    def readablefile(cls, filename):
-        """
-        Returns True/False if filename exists and is readable
-        """
-        try:
-            test = file(filename, "rb")
-            test.close()
-            return True
-        except (OSError, IOError):
-            return False
 
     def write(self, filename=None, encoding="UTF-8"):
         """
         Write current XML tree to filename, or self.name if None.
         """
+
         if filename is None:
             filename = self.name
         ElementTree.ElementTree.write(self, filename, encoding)
 
+
     def read(self, xml):
         self.__del__()
         self.__init__(xml)
 
+
 class Sub(object):
     """String substituter using string.Template"""
 
     def __init__(self, **mapping):
         """Initialize substitution mapping."""
+
         self._mapping = mapping
 
+
     def substitute(self, text):
         """
         Use string.safe_substitute on text and return the result
 
         @param: text: string to substitute
         """
+
         return string.Template(text).safe_substitute(**self._mapping)
 
 
@@ -179,14 +204,16 @@ class TemplateXMLTreeBuilder(ElementTree.XMLTreeBuilder, 
Sub):
 
         @param: **mapping: values to be substituted for ${key} in XML input
         """
+
         Sub.__init__(self, **mapping)
         ElementTree.XMLTreeBuilder.__init__(self, target=self.BuilderClass())
 
+
     def feed(self, data):
         ElementTree.XMLTreeBuilder.feed(self, self.substitute(data))
 
 
-class TemplateXML(XMLBase):
+class TemplateXML(XMLTreeFile):
     """Template-sourced XML ElementTree backed by temporary file."""
 
     ParserClass = TemplateXMLTreeBuilder
@@ -198,11 +225,13 @@ class TemplateXML(XMLBase):
         @param: xml: A filename or string containing XML
         @param: **mapping: keys/values to feed with XML to string.template
         """
+
         self.parser = self.ParserClass(**mapping)
         # ElementTree.init calls self.parse()
         super(TemplateXML, self).__init__(xml)
         # XMLBase.__init__ calls self.write() after super init
 
+
     def parse(self, source):
         """
         Parse source XML file or filename using TemplateXMLTreeBuilder
@@ -210,11 +239,14 @@ class TemplateXML(XMLBase):
         @param: source: XML file or filename
         @param: parser: ignored
         """
-        return super(XMLBase, self).parse(source, self.parser)
+
+        return super(TemplateXML, self).parse(source, self.parser)
+
 
     def restore(self):
         """
         Raise an IOError to protect the original template source.
         """
-        raise(IOError, "Protecting template source, disallowing restore to %s" 
%
+
+        raise IOError("Protecting template source, disallowing restore to %s" %
                         self.sourcefilename)
diff --git a/client/shared/xml_utils_unittest.py 
b/client/shared/xml_utils_unittest.py
index 7c79e43..58a03d5 100755
--- a/client/shared/xml_utils_unittest.py
+++ b/client/shared/xml_utils_unittest.py
@@ -43,12 +43,14 @@ class xml_test_data(unittest.TestCase):
         os.close(fd)
         self.canonicalize_test_xml()
 
+
     def tearDown(self):
         for filename in glob.glob(os.path.join('/tmp', "%s*%s" %
                                                (xml_utils.TMPPFX, 
xml_utils.TMPSFX)
                                               )):
             os.unlink(filename)
 
+
     def canonicalize_test_xml(self):
         et = ElementTree.parse(self.XMLFILE)
         et.write(self.XMLFILE, encoding="UTF-8")
@@ -70,6 +72,7 @@ class test_TempXMLFile(xml_test_data):
         self.assert_(filename.startswith(xml_utils.TMPPFX))
         self.assert_(filename.endswith(xml_utils.TMPSFX))
 
+
     def test_test_TempXMLFile_canread(self):
         tmpf = xml_utils.TempXMLFile()
         tmpf.write(self.XMLSTR)
@@ -78,6 +81,7 @@ class test_TempXMLFile(xml_test_data):
         self.assertEqual(stuff, self.XMLSTR)
         del tmpf
 
+
     def test_TempXMLFile_implicit(self):
         def out_of_scope_tempxmlfile():
             tmpf = xml_utils.TempXMLFile()
@@ -103,14 +107,17 @@ class test_XMLBackup(xml_test_data):
         s = f.read()
         return s == self.XMLSTR
 
+
     def test_backup_filename(self):
         xmlbackup = self.class_to_test(self.XMLFILE)
         self.assertEqual(xmlbackup.sourcefilename, self.XMLFILE)
 
+
     def test_backup_file(self):
         xmlbackup = self.class_to_test(self.XMLFILE)
         self.assertTrue(self.is_same_contents(xmlbackup.name))
 
+
     def test_rebackup_file(self):
         xmlbackup = self.class_to_test(self.XMLFILE)
         oops = file(xmlbackup.name, "wb")
@@ -120,6 +127,7 @@ class test_XMLBackup(xml_test_data):
         xmlbackup.backup()
         self.assertTrue(self.is_same_contents(xmlbackup.name))
 
+
     def test_restore_file(self):
         xmlbackup = self.class_to_test(self.XMLFILE)
         # nuke source
@@ -127,6 +135,7 @@ class test_XMLBackup(xml_test_data):
         xmlbackup.restore()
         self.assertTrue(self.is_same_contents(xmlbackup.name))
 
+
     def test_remove_backup_file(self):
         xmlbackup = self.class_to_test(self.XMLFILE)
         filename = xmlbackup.name
@@ -134,6 +143,7 @@ class test_XMLBackup(xml_test_data):
         del xmlbackup
         self.assertRaises(OSError, os.unlink, filename)
 
+
     def test_TempXMLBackup_implicit(self):
         def out_of_scope_xmlbackup():
             tmpf = self.class_to_test(self.XMLFILE)
@@ -143,6 +153,7 @@ class test_XMLBackup(xml_test_data):
         self.assertTrue(self.is_same_contents(filename))
         os.unlink(filename)
 
+
     def test_TempXMLBackup_exception_exit(self):
         tmpf = self.class_to_test(self.XMLFILE)
         filename = tmpf.name
@@ -151,6 +162,7 @@ class test_XMLBackup(xml_test_data):
         self.assertTrue(self.is_same_contents(filename))
         os.unlink(filename)
 
+
     def test_TempXMLBackup_unexception_exit(self):
         tmpf = self.class_to_test(self.XMLFILE)
         filename = tmpf.name
@@ -159,23 +171,30 @@ class test_XMLBackup(xml_test_data):
         self.assertRaises(OSError, os.unlink, filename)
 
 
-class test_XMLBase(test_XMLBackup):
+class test_XMLTreeFile(test_XMLBackup):
 
-    class_to_test = xml_utils.XMLBase
+    class_to_test = xml_utils.XMLTreeFile
 
     def test_init_str(self):
         xml = self.class_to_test(self.XMLSTR)
-        self.assert_(xml.tempsource is not None)
+        self.assert_(xml.sourcefilename is not None)
+        self.assertEqual(xml.sourcebackupfile.name,
+                         xml.sourcefilename)
+
 
     def test_init_xml(self):
         xml = self.class_to_test(self.XMLFILE)
-        self.assert_(xml.tempsource is None)
+        self.assert_(xml.sourcefilename is not None)
+        self.assertEqual(xml.sourcebackupfile.name,
+                         xml.sourcefilename)
+
 
     def test_restore_from_string(self):
         xmlbackup = self.class_to_test(self.XMLSTR)
         os.unlink(xmlbackup.sourcefilename)
         xmlbackup.restore()
-        self.assertTrue(self.is_same_contents(xmlbackup.tempsource.name))
+        self.assertTrue(self.is_same_contents(xmlbackup.sourcefilename))
+
 
     def test_restore_from_file(self):
         xmlbackup = self.class_to_test(self.XMLFILE)
@@ -183,6 +202,7 @@ class test_XMLBase(test_XMLBackup):
         xmlbackup.restore()
         self.assertTrue(self.is_same_contents(xmlbackup.name))
 
+
     def test_write_default(self):
         xmlbackup = self.class_to_test(self.XMLFILE)
         wordsize = xmlbackup.find('guest/arch/wordsize')
@@ -192,6 +212,7 @@ class test_XMLBase(test_XMLBackup):
         xmlbackup.write()
         self.assertFalse(self.is_same_contents(xmlbackup.name))
 
+
     def test_write_other(self):
         xmlbackup = self.class_to_test(self.XMLFILE)
         otherfile = xml_utils.TempXMLFile()
@@ -199,6 +220,7 @@ class test_XMLBase(test_XMLBackup):
         otherfile.close()
         self.assertTrue(self.is_same_contents(otherfile.name))
 
+
     def test_write_other_changed(self):
         xmlbackup = self.class_to_test(self.XMLSTR)
         otherfile = xml_utils.TempXMLFile()
@@ -211,6 +233,7 @@ class test_XMLBase(test_XMLBackup):
         self.canonicalize_test_xml()
         self.assertTrue(self.is_same_contents(otherfile.name))
 
+
     def test_read_other_changed(self):
         xmlbackup = self.class_to_test(self.XMLSTR)
         wordsize = xmlbackup.find('guest/arch/wordsize')
@@ -236,10 +259,12 @@ class test_templatized_xml(xml_test_data):
         self.RESULTCHECK = """<bar baz="foo">foobarbaz</bar>"""
         super(test_templatized_xml, self).setUp()
 
+
     def test_sub(self):
         sub = xml_utils.Sub(**self.MAPPING)
         self.assertEqual(sub.substitute(self.FULLREPLACE), self.RESULTCHECK)
 
+
     def test_MappingTreeBuilder_standalone(self):
         txtb = xml_utils.TemplateXMLTreeBuilder(**self.MAPPING)
         txtb.feed(self.FULLREPLACE)
@@ -247,6 +272,7 @@ class test_templatized_xml(xml_test_data):
         result = ElementTree.tostring(et)
         self.assertEqual(result, self.RESULTCHECK)
 
+
     def test_TemplateXMLTreeBuilder_nosub(self):
         txtb = xml_utils.TemplateXMLTreeBuilder()
         # elementree pukes on identifiers starting with $
@@ -255,12 +281,14 @@ class test_templatized_xml(xml_test_data):
         result = ElementTree.tostring(et)
         self.assertEqual(result, self.RESULTCHECK)
 
+
     def test_TemplateXML(self):
         tx = xml_utils.TemplateXML(self.FULLREPLACE, **self.MAPPING)
         et = ElementTree.ElementTree(None, tx.name)
         check = ElementTree.tostring(et.getroot())
         self.assertEqual(check, self.RESULTCHECK)
 
+
     def test_restore_fails(self):
         testmapping = {self.TEXT_REPLACE_KEY:"foobar"}
         xmlbackup = xml_utils.TemplateXML(self.XMLFILE, **testmapping)
-- 
1.7.1

_______________________________________________
Autotest-kernel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/autotest-kernel

Reply via email to