On 10/03/2012 11:47 AM, Chris Evich wrote:
+ Cleaned up many pylint/style warnings
+ Added unique extension for temp files retained due to exception
+ Unified del/exit-handling behavior
+ Added XMLTreeFile method for retrieving an XPATH to an element
+ Improved unittests

Good stuff! Two minor comments:

Signed-off-by: Chris Evich <[email protected]>
---
  client/shared/xml_utils.py          |   83 +++++++++++++++++++++++++++--------
  client/shared/xml_utils_unittest.py |   52 ++++++++++++++--------
  client/tests                        |    2 +-
  3 files changed, 98 insertions(+), 39 deletions(-)

diff --git a/client/shared/xml_utils.py b/client/shared/xml_utils.py
index 2caf0df..44c5bb7 100644
--- a/client/shared/xml_utils.py
+++ b/client/shared/xml_utils.py
@@ -48,35 +48,35 @@ import logging
  from autotest.client.shared import ElementTree

  # Also used by unittests
-TMPPFX='xml_utils_temp_'
-TMPSFX='.xml'
-EXSFX='_exception_retained'
+TMPPFX = 'xml_utils_temp_'
+TMPSFX = '.xml'
+EXSFX = '_exception_retained'
+ENCODING = "UTF-8"

  class TempXMLFile(file):
      """
      Temporary XML file auto-removed on instance del / module exit.
      """

-    def __init__(self, suffix=TMPSFX, prefix=TMPPFX, mode="wb+", buffer=1):
+    def __init__(self, suffix=TMPSFX, prefix=TMPPFX, mode="wb+", buffsz=1):
          """
          Initialize temporary XML file removed on instance destruction.

          param: suffix: temporary file's suffix
          param: prefix: temporary file's prefix
-        param: mode: file access mode
-        param: buffer: size of buffer in bytes, 1: line buffered
+        param: mode: second parameter to file()/open()
+        param: buffer: third parameter to file()/open()
          """
-        fd,path = tempfile.mkstemp(suffix=suffix, prefix=prefix)
+        fd, path = tempfile.mkstemp(suffix=suffix, prefix=prefix)
          os.close(fd)
-        super(TempXMLFile, self).__init__(path, mode, buffer)
+        super(TempXMLFile, self).__init__(path, mode, buffsz)


      def _info(self):
          """
          Inform user that file was not auto-deleted due to exceptional exit.
          """
-        logging.info("Retaining backup of %s in %s", self.sourcefilename,
-                                                     self.name + EXSFX)
+        logging.info("Retaining %s", self.name + EXSFX)


      def unlink(self):
@@ -105,7 +105,7 @@ class TempXMLFile(file):
          """
          unlink temporary file on instance delete.
          """
-
+        self.close()
          self.unlink()


@@ -128,10 +128,19 @@ class XMLBackup(TempXMLFile):


      def __del__(self):
+        # Drop reference, don't delete source!
          self.sourcefilename = None
          super(XMLBackup, self).__del__()


+    def _info(self):
+        """
+        Inform user that file was not auto-deleted due to exceptional exit.
+        """
+        logging.info("Retaining backup of %s in %s", self.sourcefilename,
+                                                     self.name + EXSFX)

^ For one, the info message here is inconsistent with the definition of _info above, so better to make them equal.

+
      def backup(self):
          """
          Overwrite temporary backup with contents of original source.
@@ -139,7 +148,8 @@ class XMLBackup(TempXMLFile):

          self.flush()
          self.seek(0)
-        shutil.copyfileobj(file(self.sourcefilename, "rb"), 
super(XMLBackup,self))
+        shutil.copyfileobj(file(self.sourcefilename, "rb"),
+                           super(XMLBackup, self))
          self.seek(0)


@@ -150,7 +160,8 @@ class XMLBackup(TempXMLFile):

          self.flush()
          self.seek(0)
-        shutil.copyfileobj(super(XMLBackup,self), file(self.sourcefilename, 
"wb+"))
+        shutil.copyfileobj(super(XMLBackup, self),
+                           file(self.sourcefilename, "wb+"))
          self.seek(0)


@@ -175,8 +186,9 @@ class XMLTreeFile(ElementTree.ElementTree, XMLBackup):
          # to hold the original content.
          try:
              # Test if xml is a valid filename
-            self.sourcebackupfile = open(xml, "rb")
+            self.sourcebackupfile = file(xml, "rb")
              self.sourcebackupfile.close()
+            # XMLBackup init will take care of creating a copy
          except (IOError, OSError):
              # Assume xml is a string that needs a temporary source file
              self.sourcebackupfile = TempXMLFile()
@@ -191,6 +203,11 @@ class XMLTreeFile(ElementTree.ElementTree, XMLBackup):
          self.flush() # make sure it's on-disk


+    def __str__(self):
+        self.write()
+        self.flush()
+        return self.sourcebackupfile.read()
+
      def backup_copy(self):
          """Return a copy of instance, including copies of files"""
          return self.__class__(self.name)
@@ -219,6 +236,30 @@ class XMLTreeFile(ElementTree.ElementTree, XMLBackup):
              return None


+    def get_xpath(self, element):
+        """Return the XPath string formed from first-match tag names"""
+        parent_map = self.get_parent_map()
+        root = self.getroot()
+        assert root in parent_map.values()
+        if element == root:
+            return '.'
+        # List of strings reversed at end
+        path_list = []
+        while element != root:
+            # 2.7+ ElementPath supports predicates, so:
+            # element_index = list(parent_map[element]).index(element)
+            # element_index += 1 # XPath indexes are 1 based
+            # if element_index > 1:
+            #     path_list.append(u"%s[%d]" % (element.tag, element_index))
+            # else:
+            #     path_list.append(u"%s" % element.tag)
+            path_list.append(u"%s" % element.tag)
+            element = parent_map[element]

We've got to at least remove the code commented above.

+        assert element == root
+        path_list.reverse()
+        return "/".join(path_list)
+
+
      def remove(self, element):
          """
          Removes a matching subelement.
@@ -234,16 +275,18 @@ class XMLTreeFile(ElementTree.ElementTree, XMLBackup):

          @param: xpath: element name or path to remove
          """
-        self.remove(self.find(xpath))
+        self.remove(self.find(xpath)) # can't remove root


-    def write(self, filename=None, encoding="UTF-8"):
+    # This overrides the file.write() method
+    def write(self, filename=None, encoding=ENCODING):
          """
          Write current XML tree to filename, or self.name if None.
          """

          if filename is None:
              filename = self.name
+        # Avoid calling file.write() by mistake
          ElementTree.ElementTree.write(self, filename, encoding)


@@ -310,15 +353,17 @@ class TemplateXML(XMLTreeFile):
          # XMLBase.__init__ calls self.write() after super init


-    def parse(self, source):
+    def parse(self, source, parser=None):
          """
          Parse source XML file or filename using TemplateXMLTreeBuilder

          @param: source: XML file or filename
          @param: parser: ignored
          """
-
-        return super(TemplateXML, self).parse(source, self.parser)
+        if parser is None:
+            return super(TemplateXML, self).parse(source, self.parser)
+        else:
+            return super(TemplateXML, self).parse(source, parser)


      def restore(self):
diff --git a/client/shared/xml_utils_unittest.py 
b/client/shared/xml_utils_unittest.py
index 05ffd97..ebaa141 100755
--- a/client/shared/xml_utils_unittest.py
+++ b/client/shared/xml_utils_unittest.py
@@ -60,6 +60,7 @@ class xml_test_data(unittest.TestCase):
          if len(leftovers) > 0:
              self.fail('Leftover files: %s' % str(leftovers))

+
      def canonicalize_test_xml(self):
          et = ElementTree.parse(self.XMLFILE)
          et.write(self.XMLFILE, encoding="UTF-8")
@@ -68,6 +69,22 @@ class xml_test_data(unittest.TestCase):
          f.close()


+    def is_same_contents(self, filename, other=None):
+        """Compare filename contents with XMLSTR, or contents of other"""
+        try:
+            f = file(filename, "rb")
+            s = f.read()
+        except (IOError, OSError):
+            logging.warning("File %s does not exist" % filename)
+            return False
+        if other is None:
+            return s == self.XMLSTR
+        else:
+            other_f = file(other, "rb")
+            other_s = other_f.read()
+            return s == other_s
+
+
  class test_ElementTree(xml_test_data):

      def test_bundled_elementtree(self):
@@ -88,7 +105,6 @@ class test_TempXMLFile(xml_test_data):
          tmpf.seek(0)
          stuff = tmpf.read()
          self.assertEqual(stuff, self.XMLSTR)
-        del tmpf


      def test_TempXMLFile_implicit(self):
@@ -111,22 +127,6 @@ class test_XMLBackup(xml_test_data):

      class_to_test = xml_utils.XMLBackup

-
-    def is_same_contents(self, filename, other=None):
-        try:
-            f = file(filename, "rb")
-            s = f.read()
-        except (IOError, OSError):
-            logging.warning("File %s does not exist" % filename)
-            return False
-        if other is None:
-            return s == self.XMLSTR
-        else:
-            other_f = file(other, "rb")
-            other_s = other_f.read()
-            return s == other_s
-
-
      def test_backup_filename(self):
          xmlbackup = self.class_to_test(self.XMLFILE)
          self.assertEqual(xmlbackup.sourcefilename, self.XMLFILE)
@@ -245,8 +245,6 @@ class test_XMLTreeFile(test_XMLBackup):
          self.assertFalse(self.is_same_contents(bu_tmps.name, tmps.name))
          self.assertTrue(self.is_same_contents(bu_tmpf.name, bu_tmps.name))
          self.assertFalse(self.is_same_contents(tmpf.name, tmps.name))
-        del bu_tmpf
-        del bu_tmps


      def test_write_default(self):
@@ -297,6 +295,22 @@ class test_XMLTreeFile(test_XMLBackup):
          self.assertTrue(self.is_same_contents(otherfile.name))


+    def get_xpath_elements(self, target_path_string):
+        xmlbackup = self.class_to_test(self.XMLSTR)
+        target_element = xmlbackup.find(target_path_string)
+        test_path_string = xmlbackup.get_xpath(target_element)
+        test_element = xmlbackup.find(test_path_string)
+        return (target_element, test_element)
+
+
+    def test_get_xpath(self):
+        # 2.6 ElementPath doesn't support predicates as in 2.7 :(
+        # (it blindly returns the first match)
+        self.assertEqual(*self.get_xpath_elements('guest/arch/wordsize'))
+        self.assertEqual(*self.get_xpath_elements('guest/arch/machine'))
+        self.assertEqual(*self.get_xpath_elements('host/cpu/arch'))
+
+
  class test_templatized_xml(xml_test_data):

      def setUp(self):
diff --git a/client/tests b/client/tests
index 4ebba12..de7e51b 160000
--- a/client/tests
+++ b/client/tests
@@ -1 +1 @@
-Subproject commit 4ebba1264bdcddfe738c109e3fde5395278ba31f
+Subproject commit de7e51b6d917f4d30214eb9845f542c0f6a60b5f


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

Reply via email to