Commits

Ronny Pfannschmidt  committed eb575d4

switch filewriter to etree (slightly broken)

* indent isn't correct
* small encoding issues (see hidden character test)
* what the heck is wrong with tests/test_saveproperties.py

  • Participants
  • Parent commits bc12a88

Comments (0)

Files changed (7)

 depends()
 targets(
     test $ nosetests -A 'not skip' -d
+    test.less $ nosetests -A 'not skip' -d 2>&1 |less
 )

File gazpacho/filewriter.py

 from xml.sax.saxutils import escape
 import gobject
 import gtk
+from lxml.etree import ElementTree, Element, SubElement, tostring
+
+import re
+
 
 from gazpacho import util
 from gazpacho.choice import enum_to_string, flags_to_string
 from gazpacho.gadget import Gadget
 from gazpacho.properties import prop_registry, ObjectType
 
-def write_xml(file, xml_node, indent=0, indent_increase=4):
-    if xml_node.nodeType == xml_node.TEXT_NODE:
-        file.write(xml_node.data)
-        return
-    elif xml_node.nodeType == xml_node.CDATA_SECTION_NODE:
-        file.write('<![CDATA[%s]]>' % xml_node.data)
-        return
 
-    file.write(' '*indent)
+def pretty(tree):
+    data = tostring(tree, pretty_print=True)
+    #XXX: nasty hack to set indent to 4 instead of 2
+    return re.sub(r'^\s+(?m)', lambda m:m.group()*2, data)
 
-    file.write('<%s' % xml_node.tagName)
-    if len(xml_node.attributes) > 0:
-        attr_string = ' '.join(['%s="%s"' % (n, v)
-                                    for n, v in xml_node.attributes.items()])
-        file.write(' ' + attr_string)
-
-    children = [a for a in xml_node.childNodes
-                    if a.nodeType != a.ATTRIBUTE_NODE]
-    if children:
-        has_text_child = False
-        for child in children:
-            if child.nodeType in (child.TEXT_NODE,
-                                  child.CDATA_SECTION_NODE):
-                has_text_child = True
-                break
-
-        if has_text_child:
-            file.write('>')
-        else:
-            file.write('>\n')
-        for child in children:
-            write_xml(file, child, indent+indent_increase, indent_increase)
-
-        if not has_text_child:
-            file.write(' '*indent)
-        file.write('</%s>\n' % xml_node.tagName)
-    else:
-        file.write('/>\n')
 
 class XMLWriter:
     """
         for model in models:
             self._write_model(root, model)
         self._write_widgets(root, widgets, uim)
-        self.write_node(path, root)
+        self.write_node(root, path)
 
     def _write_header(self, f):
         f.write('<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->\n')
             f.write('<!DOCTYPE glade-interface SYSTEM "%s">\n' % doctype)
 
     # FIXME: Should this really be exported
-    def write_node(self, path, root):
+    def write_node(self, root, path):
         f = file(path, 'w')
+        #XXX: ugly
         self._write_header(f)
-        write_xml(f, root)
+        f.write(pretty(root))
         f.close()
 
     def _create_root(self):
             tag = 'glade-interface'
         else:
             tag = 'interface'
+        return Element(tag)
 
-        element = self._doc.createElement(tag)
-        return self._doc.appendChild(element)
 
     def serialize_node(self, gadget):
         root = self._write_root()
         if widgets:
             uim_node = self._write_ui_manager(gadget.project.uim, widgets)
             if uim_node:
-                root.appendChild(uim_node)
+                root.append(uim_node)
 
         # save the Model for TreeViews
         if isinstance(gadget.widget, gtk.TreeView):
             model = gadget.adaptor.get_model(gadget)
             self._write_model(root, model)
 
-        root.appendChild(self._write_widget(gadget))
+        root.append(self._write_widget(gadget))
 
-        return self._doc.documentElement
+        return root
 
     def serialize(self, gadget, skip_external_references=False):
 
             self._skip_external_references = True
             self._all_widgets = get_all_widgets(gadget.widget)
 
-        fp = cStringIO.StringIO()
         node = self.serialize_node(gadget)
-        write_xml(fp, node)
-        fp.seek(0)
 
         # reset context dependent states
         self._skip_external_references = False
         self._all_widgets = []
 
-        return fp.read()
+        return tostring(node, pretty_print=True)
 
     def serialize_widgets(self, widgets, sizegroups, models, uim):
-        fp = cStringIO.StringIO()
         root = self._write_root(widgets)
         self._write_sizegroups(root, sizegroups)
         for model in models:
             self._write_model(root, model)
         self._write_widgets(root, widgets, uim)
-        write_xml(fp, root)
-        fp.seek(0)
+        return tostring(node, pretty_print=True)
 
-        return fp.read()
 
     def _get_requirements(self, widgets):
         # check what modules are the gadgets using
         root = self._create_root()
 
         if domain:
-            root.setAttribute('domain', domain)
+            root.set('domain', domain)
 
         # Disable require for now, see bug #344672
         if 0: #self._version == "libglade" and widgets:
             for module in self._get_requirements(widgets):
-                n = self._doc.createElement('requires')
-                n.setAttribute('lib', module)
-                root.appendChild(n)
+                SubElement(root, 'requires', lib=module)
 
         return root
 
-    def _create_object(self, class_name, name):
+    def _create_object(self, class_name, name, constructor=None):
         if self._version == "gtkbuilder":
             tag = 'object'
         else:
             tag = 'widget'
-        node = self._doc.createElement(tag)
-        node.setAttribute('class', class_name)
-        node.setAttribute('id', name)
+        node = Element(tag)
+        if constructor is not None:
+            node.set('constructor', constructor)
+        node.set('class', class_name)
+        node.set('id', name)
         return node
 
     def _write_sizegroups(self, root, sizegroups):
         for sizegroup in sizegroups:
             node = self._create_object('GtkSizeGroup', sizegroup.name)
-            root.appendChild(node)
-            prop_node = self._doc.createElement('property')
-            node.appendChild(prop_node)
-            prop_node.setAttribute('name', 'mode')
-            text = self._doc.createTextNode(enum_to_string(
-                sizegroup.mode, enum=gtk.SizeGroupMode))
-            prop_node.appendChild(text)
+            root.append(node)
+            prop_node = SubElement(node, 'property', name='mode')
+            prop_node.text = enum_to_string(
+                sizegroup.mode, enum=gtk.SizeGroupMode)
 
             # This needs to be cleaned up, ideally moved to sizegroup.py
             if self._version == 'gtkbuilder':
-                widgets = self._doc.createElement('widgets')
-                node.appendChild(widgets)
+                widgets = SubElement(node, 'widgets')
                 for gadget in sizegroup.get_gadgets():
-                    widget = self._doc.createElement('widget')
-                    widget.setAttribute('name', gadget.name)
-                    widgets.appendChild(widget)
+                    SubElement(widgets, 'widget', name=gadget.name)
 
     def _write_ui_manager(self, uim, widgets):
         """Create a XML node with the information of this UIManager
         # XXX don't save all the action groups, only those ones
         # that are in use
         for action_group in action_groups:
-            child_node = self._doc.createElement('child')
-            uim_node.appendChild(child_node)
-            child_node.appendChild(self._write_action_group(action_group))
+            child_node = SubElement(uim_node, 'child')
+            child_node.append(self._write_action_group(action_group))
 
         # Append uimanager
         gadgets = []
                 raise AssertionError("There is no gadget for %s" % widget)
             gadgets.append(gadget)
 
-        nodes = uim.save_ui_definitions(self._doc, gadgets, self._version)
+        nodes = uim.save_ui_definitions( gadgets, self._version)
         for ui_node in nodes:
-            uim_node.appendChild(ui_node)
+            print pretty(ui_node)
+            uim_node.append(ui_node)
 
         return uim_node
 
         action_names.sort()
 
         for action_name in action_names:
-            child_node = self._doc.createElement('child')
-            node.appendChild(child_node)
+            child_node = SubElement(node, 'child')
             action = action_group.get_action(action_name)
-            child_node.appendChild(self._write_action(action))
+            child_node.append(self._write_action(action))
 
         return node
 
                  default_key, domain) = stock_info
 
         node = self._write_property('name', action.name)
-        action_node.appendChild(node)
+        action_node.append(node)
 
         # default_label is translated, so compare against the
         # untranslated version sent through dgettext()
         if default_label != dgettext(domain, action.label):
             node = self._write_property('label', action.label, True)
-            action_node.appendChild(node)
+            action_node.append(node)
 
         if action.label != action.short_label:
             node = self._write_property('short_label', action.short_label,
                                         True)
-            action_node.appendChild(node)
+            action_node.append(node)
 
         if action.is_important:
             node = self._write_property('is_important', 'True')
-            action_node.appendChild(node)
+            action_node.append(node)
 
         if action.tooltip:
             node = self._write_property('tooltip', action.tooltip, True)
-            action_node.appendChild(node)
+            action_node.append(node)
 
         if action.stock_id:
             node = self._write_property('stock_id', action.stock_id)
-            action_node.appendChild(node)
+            action_node.append(node)
 
         if action.callback:
             signal_node = self._write_signal('activate', action.callback)
-            action_node.appendChild(signal_node)
+            action_node.append(signal_node)
 
         if action.accelerator and self._version != 'gtkbuilder':
             key, mask = gtk.accelerator_parse(action.accelerator)
             if key != default_key or mask != default_mask:
                 node = self._write_property('accelerator',
                                             action.accelerator)
-                action_node.appendChild(node)
+                action_node.append(node)
 
         if action.icon_name:
             node = self._write_property('icon_name', action.icon_name)
-            action_node.appendChild(node)
+            action_node.append(node)
             
 
         return action_node
 
         model_node = self._create_object(model.get_type_name(),
                                          model.name)
-        root.appendChild(model_node)
+        root.append(model_node)
 
         # first we save the types
         column_type_names = model.get_column_type_names()
         if column_type_names:
-            columns_node = self._doc.createElement('columns')
-            model_node.appendChild(columns_node)
+            columns_node = Element('columns')
+            model_node.append(columns_node)
             for type_name in column_type_names:
-                column_node = self._doc.createElement('column')
-                column_node.setAttribute('type', type_name)
-                columns_node.appendChild(column_node)
+                column_node = Element('column')
+                column_node.set('type', type_name)
+                columns_node.append(column_node)
 
         # then the data
         store = model.get_model()
         if len(store):
             data_node = self._write_list_store_data(store,
                                                     len(column_type_names))
-            model_node.appendChild(data_node)
+            model_node.append(data_node)
 
     def _write_list_store_data(self, store, n_columns):
         # <data>
         #   </row>
         # </data>
 
-        data_node = self._doc.createElement('data')
+        data_node = Element('data')
         for row in store:
-            row_node = self._doc.createElement('row')
-            data_node.appendChild(row_node)
+            row_node = SubElement(data_node, 'row')
 
             for i in range(n_columns):
-                col_node = self._doc.createElement('col')
-                row_node.appendChild(col_node)
-                col_node.setAttribute('id', str(i))
+                col_node = SubElement(row_node, 'col', id=str(i))
 
                 data = row[i]
                 if data is None:
                     text = ''
                 else:
                     text = escape(str(row[i]))
-                node = self._doc.createTextNode(text)
-                col_node.appendChild(node)
+                col_node.text = text
 
         return data_node
 
     def _write_widgets(self, node, widgets, uim):
         ui_node = self._write_ui_manager(uim, widgets)
         if ui_node:
-            node.appendChild(ui_node)
+            node.append(ui_node)
 
         # Append toplevel widgets. Each widget then takes care of
         # appending its children
                 continue
 
             wnode = self._write_widget(gadget)
-            node.appendChild(wnode)
+            node.append(wnode)
 
         return node
 
             for signal in signals:
                 signal_node = self._write_signal(signal.name, signal.handler,
                                                  signal.after, signal.object)
-                node.appendChild(signal_node)
+                node.append(signal_node)
 
         # Children
 
                     continue
                 child_node = self._write_child(child_gadget)
 
-            node.appendChild(child_node)
+            node.append(child_node)
 
         gadget.maintain_gtk_properties = False
 
                     comment = prop.i18n_comment
             property_node = self._write_property(name, value, translatable,
                                                  context, comment)
-            parent_node.appendChild(property_node)
+            parent_node.append(property_node)
 
     def _write_signal(self, name, handler, after=False, object=None):
         # <signal name="..." handler="..." after="..." object="..."/>
-        signal_node = self._doc.createElement('signal')
-        signal_node.setAttribute('name', name)
-        signal_node.setAttribute('handler', handler)
+        signal_node = Element('signal')
+        signal_node.set('handler', handler)
+        signal_node.set('name', name)
         if after:
-            signal_node.setAttribute('after', 'True')
+            signal_node.set('after', 'True')
         if object:
-            signal_node.setAttribute('object', object)
+            signal_node.set('object', object)
         return signal_node
 
     def _write_basic_information(self, gadget):
         else:
             type_name = gadget.adaptor.type_name
 
-        node = self._create_object(type_name, gadget.name)
-        if gadget.constructor:
-            constructor = gadget.constructor
-            node.setAttribute('constructor', constructor)
+        node = self._create_object(type_name, gadget.name, gadget.constructor)
         return node
 
     def _write_child(self, child_gadget):
         #   <child internal-name="foo">
         # </child>
 
-        child_node = self._doc.createElement('child')
+        child_node = Element('child')
 
         if child_gadget.internal_name is not None:
-            child_node.setAttribute('internal-child',
+            child_node.set('internal-child',
                                     child_gadget.internal_name)
 
         child = self._write_widget(child_gadget)
-        child_node.appendChild(child)
+        child_node.append(child)
 
         # Append the packing properties
-        packing_node = self._doc.createElement('packing')
+        packing_node = Element('packing')
         self._write_properties(packing_node, child_gadget, child=True)
-
-        if packing_node.childNodes:
-            child_node.appendChild(packing_node)
+        if len(packing_node):
+            child_node.append(packing_node)
 
         return child_node
 
         #   <placeholder>
         # </child>
 
-        child_node = self._doc.createElement('child')
-        placeholder_node = self._doc.createElement('placeholder')
-        child_node.appendChild(placeholder_node)
+        child_node = Element('child')
+        SubElement(child_node, 'placeholder')
 
         # we need to write the packing properties of the placeholder.
         # otherwise the container gets confused when loading its
         # children
         node = self._write_placeholder_properties(widget)
         if node:
-            child_node.appendChild(node)
+            child_node.append(node)
 
         return child_node
 
         pspecs = gtk.container_class_list_child_properties(parent)
         if not pspecs:
             return
-        packing_node = self._doc.createElement('packing')
+        packing_node = Element('packing')
         for pspec in pspecs:
             value = parent.child_get_property(placeholder, pspec.name)
             if value == pspec.default_value:
                 v = escape(str(value))
 
             prop_node = self._write_property(prop_name, v)
-            packing_node.appendChild(prop_node)
+            packing_node.append(prop_node)
 
         return packing_node
 
         #           context="yes|no"
         #           translatable="yes|no">...</property>
 
-        node = self._doc.createElement('property')
+        node = Element('property')
 
         # We should change each '-' by '_' on the name of the property
         # put the name="..." part on the <property ...> tag
-        node.setAttribute('name', name)
+        node.set('name', name)
 
         # Only write context and comment if translatable is
         # enabled, to mimic glade-2
         if translatable:
-            node.setAttribute('translatable', 'yes')
             if context and self._version != 'gtkbuilder':
-                node.setAttribute('context', 'yes')
+                node.set('context', 'yes')
             if comment:
-                node.setAttribute('comment', comment)
+                node.set('comment', comment)
+            node.set('translatable', 'yes')
 
-        text = self._doc.createTextNode(escape(value))
-        node.appendChild(text)
+        node.text = unicode(value)
         return node
 
     def _write_cell_renderer(self, cell_renderer, column):
-        child_node = self._doc.createElement('child')
+        child_node = Element('child')
         name = column.get_name_for_cell_renderer(cell_renderer)
         cell_node = self._create_object(gobject.type_name(cell_renderer), name)
-        child_node.appendChild(cell_node)
+        child_node.append(cell_node)
 
-        attributes_node = self._doc.createElement('attributes')
-        child_node.appendChild(attributes_node)
+        attributes_node = Element('attributes')
+        child_node.append(attributes_node)
 
         attributes = column.get_attributes(cell_renderer)
         for key, value in attributes.items():
-            attr_node = self._doc.createElement('attribute')
-            attr_node.setAttribute('name', key)
-            text = self._doc.createTextNode(escape(str(value)))
-            attr_node.appendChild(text)
-            attributes_node.appendChild(attr_node)
+            attr_node = SubElement(attributes_node, 'attribute')
+            attr_node.set('name', key)
+            attr_node.text = unicode(value)
 
         return child_node
 

File gazpacho/uimanager.py

 from xml.dom import minidom
 
 import gtk
+from lxml import etree
+from .filewriter import pretty
 
 from gazpacho.gaction import GAction, GActionGroup
 
 
     def _change_ui_definition(self, xml_string, new_name):
         "Change a ui definition of a widget to use a new_name for the widget"
-        doc = minidom.parseString(xml_string)
-        doc.documentElement.setAttribute('action', new_name)
-        doc.documentElement.setAttribute('name', new_name)
-        return doc.documentElement.toxml()
+        data = etree.fromstring(xml_string)
+        data.set('action', new_name)
+        data.set('name', new_name)
+        return etree.tostring(data)
 
     def load(self, loader):
         """Get the UIManager of the loader and copy its content into
         widget = self.get_widget(gadget)
         gadget.setup_widget(widget)
 
-    def save_ui_definitions(self, document, gadgets, version):
+    def save_ui_definitions(self, gadgets, version):
         """Return a list of XML nodes with the ui definitions of the
         gadgets we need to save
         """
 
         nodes = []
         for ui_name, xml_list in uis.items():
-            ui_node = document.createElement('ui')
-            ui_node.setAttribute('id', ui_name)
+            ui_node = etree.Element('ui')
+            if ui_name:
+                ui_node.set('id', ui_name)
 
-            uidef_node = document.createElement('ui')
-            text_node = minidom.Text()
-            text_node.data = '\n'
-            uidef_node.appendChild(text_node)
+            uidef_node = etree.Element('ui')
+            uidef_node
             for xml_string in xml_list:
-                tmp = minidom.parseString(xml_string)
-                uidef_node.appendChild(tmp.documentElement)
-            text_node = minidom.Text()
-            text_node.data = '\n'
-            uidef_node.appendChild(text_node)
+                tmp = etree.fromstring(xml_string)
+                uidef_node.append(tmp)
 
             if version == 'gtkbuilder':
-                strip_text_nodes(uidef_node)
+                #XXX: broke?
+                #strip_text_nodes(uidef_node)
                 nodes.append(uidef_node)
             else:
-                child = document.createCDATASection(uidef_node.toxml())
-                ui_node.appendChild(child)
+                import re
+
+                nice1 = pretty(uidef_node)[:-1]
+                print nice1
+                nice2 = re.sub(r'^\s+(?m)',
+                        lambda m:m.group()[::2],
+                        nice1)
+                child = etree.CDATA(nice2)
+
+                ui_node.text = child
                 nodes.append(ui_node)
 
         return nodes

File tests/test_button.py

 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 import gtk
+from lxml import etree
 
 from gazpacho.filewriter import XMLWriter
 from gazpacho.project import GazpachoObjectBuilder
 
 import common
 
+find_props = etree.XPath('//property')
+
 class GtkButtonTest(common.GazpachoTest):
 
     def setUp(self):
     def serialize(self):
         xw = XMLWriter(project=self.project)
         node = xw.serialize_node(self.button)
-        self.properties = node.getElementsByTagName('property')
+        self.properties = find_props(node)
         self.project.save(__file__+'.glade')
         ob = GazpachoObjectBuilder(filename=__file__+'.glade')
         self.remove_file(__file__+'.glade')
 
         xw = XMLWriter()
         node = xw.serialize_node(button)
-        xw.write_node(__file__+'.glade', node)
+        xw.write_node(node, __file__+'.glade')
         ob = GazpachoObjectBuilder(filename=__file__+'.glade')
         button = ob.get_widget('button1')
         self.failUnless(button != None, 'button not saved')
     def save_and_load(self):
         xw = XMLWriter(project=self.project)
         node = xw.serialize_node(self.button)
-        xw.write_node(__file__+'.glade', node)
+        xw.write_node(node, __file__+'.glade')
         ob = GazpachoObjectBuilder(filename=__file__+'.glade')
         button = ob.get_widget('button1')
         self.remove_file(__file__+'.glade')

File tests/test_filewriter.py

+from gazpacho.filewriter import pretty
+
+from lxml.etree import Element, SubElement
+
+def test_file_writer():
+    root = Element('root')
+    SubElement(root, 'test')
+    SubElement(root, 'test', name="test")
+
+    nice = pretty(root)
+    print nice
+    print
+
+    result = '<root>\n    <test/>\n    <test name="test"/>\n</root>\n'
+    print result
+    assert nice == result, "oo"

File tests/test_glade.py

 glade_files = glob(os.path.join(glade_dir, 'glade', '*.glade'))
 
 class BaseTest(unittest.TestCase):
-    def _test_gladefile(self, filename):
+    def _check_gladefile(self, filename):
         tmp = filename + '.tmp'
         if os.path.exists(tmp):
             os.unlink(tmp)
     testname = os.path.basename(glade_file)[:-6]
     if testname in DISABLED:
         continue
-    func = lambda self, f=glade_file: self._test_gladefile(f)
+    func = lambda self, f=glade_file: self._check_gladefile(f)
     name = 'test_%s' % testname
     func.__name__ = name
     if testname in SKIPPED:

File tests/test_uim.py

 
         new_pathname = pathname + '.tmp'
         xw = XMLWriter()
-        xw.write_node(new_pathname, node)
+        xw.write_node(node, new_pathname)
 
         diff_files(pathname, new_pathname)