Fixed support for defining python runtime code using sections like in c_ext
authorLaurent Bessard
Mon, 13 May 2013 14:31:23 +0200
changeset 1124 b1705000eba1
parent 1123 55ed55ef7aea
child 1125 1b1472e76f07
Fixed support for defining python runtime code using sections like in c_ext
CodeFileTreeNode.py
c_ext/c_ext.py
code_file.xsd
editors/CodeFileEditor.py
py_ext/PythonFileCTNMixin.py
py_ext/py_ext.py
wxglade_hmi/wxglade_hmi.py
xmlclass/__init__.py
xmlclass/xmlclass.py
--- a/CodeFileTreeNode.py	Sun May 12 23:32:30 2013 +0200
+++ b/CodeFileTreeNode.py	Mon May 13 14:31:23 2013 +0200
@@ -2,26 +2,82 @@
 from xml.dom import minidom
 import cPickle
 
-from xmlclass import *
+from xmlclass import GenerateClassesFromXSDstring, UpdateXMLClassGlobals
 
 from PLCControler import UndoBuffer
-from editors.CodeFileEditor import SECTIONS_NAMES
 
-CodeFileClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "code_file.xsd"))
+CODEFILE_XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <xsd:element name="%(codefile_name)s">
+    <xsd:complexType>
+      <xsd:sequence>
+        %(includes_section)s
+        <xsd:element name="variables">
+          <xsd:complexType>
+            <xsd:sequence>
+              <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
+                <xsd:complexType>
+                  <xsd:attribute name="name" type="xsd:string" use="required"/>
+                  <xsd:attribute name="type" type="xsd:string" use="required"/>
+                  <xsd:attribute name="class" use="optional">
+                    <xsd:simpleType>
+                      <xsd:restriction base="xsd:string">
+                        <xsd:enumeration value="input"/>
+                        <xsd:enumeration value="memory"/>
+                        <xsd:enumeration value="output"/>
+                      </xsd:restriction>
+                    </xsd:simpleType>
+                  </xsd:attribute>
+                  <xsd:attribute name="initial" type="xsd:string" use="optional" default=""/>
+                </xsd:complexType>
+              </xsd:element>
+            </xsd:sequence>
+          </xsd:complexType>
+        </xsd:element>
+        %(sections)s
+      </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+  <xsd:complexType name="CodeText">
+    <xsd:annotation>
+      <xsd:documentation>Formatted text according to parts of XHTML 1.1</xsd:documentation>
+    </xsd:annotation>
+    <xsd:sequence>
+      <xsd:any namespace="http://www.w3.org/1999/xhtml" processContents="lax"/>
+    </xsd:sequence>
+  </xsd:complexType>
+</xsd:schema>"""
+
+SECTION_TAG_ELEMENT = "<xsd:element name=\"%s\" type=\"CodeText\"/>"
 
 class CodeFile:
     
+    CODEFILE_NAME = "CodeFile"
+    SECTIONS_NAMES = []
+    
     def __init__(self):
+        sections_str = {"codefile_name": self.CODEFILE_NAME}
+        if "includes" in self.SECTIONS_NAMES:
+            sections_str["includes_section"] = SECTION_TAG_ELEMENT % "includes"
+        else:
+            sections_str["includes_section"] = ""
+        sections_str["sections"] = "\n".join(
+            [SECTION_TAG_ELEMENT % name
+             for name in self.SECTIONS_NAMES if name != "includes"])
+        
+        self.CodeFileClasses = GenerateClassesFromXSDstring(
+            CODEFILE_XSD % sections_str)
+        
         filepath = self.CodeFileName()
         
-        self.CodeFile = CodeFileClasses["CodeFile"]()
+        self.CodeFile = self.CodeFileClasses[self.CODEFILE_NAME]()
         if os.path.isfile(filepath):
             xmlfile = open(filepath, 'r')
             tree = minidom.parse(xmlfile)
             xmlfile.close()
             
             for child in tree.childNodes:
-                if child.nodeType == tree.ELEMENT_NODE and child.nodeName in ["CodeFile", "CFile"]:
+                if child.nodeType == tree.ELEMENT_NODE and child.nodeName in [self.CODEFILE_NAME]:
                     self.CodeFile.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"])
                     self.CreateCodeFileBuffer(True)
         else:
@@ -37,7 +93,7 @@
     def SetVariables(self, variables):
         self.CodeFile.variables.setvariable([])
         for var in variables:
-            variable = CodeFileClasses["variables_variable"]()
+            variable = self.CodeFileClasses["variables_variable"]()
             variable.setname(var["Name"])
             variable.settype(var["Type"])
             variable.setinitial(var["Initial"])
@@ -46,33 +102,21 @@
     def GetVariables(self):
         datas = []
         for var in self.CodeFile.variables.getvariable():
-            datas.append({"Name" : var.getname(), "Type" : var.gettype(), "Initial" : var.getinitial()})
+            datas.append({"Name" : var.getname(), 
+                          "Type" : var.gettype(), 
+                          "Initial" : var.getinitial()})
         return datas
 
     def SetTextParts(self, parts):
-        for section, code_object in zip(
-            SECTIONS_NAMES,
-            [self.CodeFile.includes,
-             self.CodeFile.globals,
-             self.CodeFile.initFunction,
-             self.CodeFile.cleanUpFunction,
-             self.CodeFile.retrieveFunction,
-             self.CodeFile.publishFunction]):
-            code_object.settext(parts[section])
-        
+        for section in self.SECTIONS_NAMES:
+            section_code = parts.get(section)
+            if section_code is not None:
+                getattr(self.CodeFile, section).settext(section_code)
+    
     def GetTextParts(self):
-        parts = {}
-        for section, code_object in zip(
-            SECTIONS_NAMES,
-            [self.CodeFile.includes,
-             self.CodeFile.globals,
-             self.CodeFile.initFunction,
-             self.CodeFile.cleanUpFunction,
-             self.CodeFile.retrieveFunction,
-             self.CodeFile.publishFunction]):
-            parts[section] = code_object.gettext()
-        return parts
-                
+        return dict([(section, getattr(self.CodeFile, section).gettext())
+                     for section in self.SECTIONS_NAMES])
+            
     def CTNTestModified(self):
         return self.ChangesToSave or not self.CodeFileIsSaved()    
 
@@ -80,10 +124,7 @@
         filepath = self.CodeFileName()
         
         text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
-        extras = {"xmlns":"http://www.w3.org/2001/XMLSchema",
-                  "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
-                  "xsi:schemaLocation" : "code_file.xsd"}
-        text += self.CodeFile.generateXMLText("CodeFile", 0, extras)
+        text += self.CodeFile.generateXMLText(self.CODEFILE_NAME, 0)
 
         xmlfile = open(filepath,"w")
         xmlfile.write(text.encode("utf-8"))
@@ -103,25 +144,33 @@
 #                      Current Buffering Management Functions
 #-------------------------------------------------------------------------------
 
+    def cPickle_loads(self, str_obj):
+        UpdateXMLClassGlobals(self.CodeFileClasses)
+        return cPickle.loads(str_obj)
+
+    def cPickle_dumps(self, obj):
+        UpdateXMLClassGlobals(self.CodeFileClasses)
+        return cPickle.dumps(obj)
+
     """
     Return a copy of the codefile model
     """
     def Copy(self, model):
-        return cPickle.loads(cPickle.dumps(model))
+        return self.cPickle_loads(self.cPickle_dumps(model))
 
     def CreateCodeFileBuffer(self, saved):
         self.Buffering = False
-        self.CodeFileBuffer = UndoBuffer(cPickle.dumps(self.CodeFile), saved)
+        self.CodeFileBuffer = UndoBuffer(self.cPickle_dumps(self.CodeFile), saved)
 
     def BufferCodeFile(self):
-        self.CodeFileBuffer.Buffering(cPickle.dumps(self.CodeFile))
+        self.CodeFileBuffer.Buffering(self.cPickle_dumps(self.CodeFile))
     
     def StartBuffering(self):
         self.Buffering = True
         
     def EndBuffering(self):
         if self.Buffering:
-            self.CodeFileBuffer.Buffering(cPickle.dumps(self.CodeFile))
+            self.CodeFileBuffer.Buffering(self.cPickle_dumps(self.CodeFile))
             self.Buffering = False
     
     def MarkCodeFileAsSaved(self):
@@ -133,10 +182,10 @@
         
     def LoadPrevious(self):
         self.EndBuffering()
-        self.CodeFile = cPickle.loads(self.CodeFileBuffer.Previous())
+        self.CodeFile = self.cPickle_loads(self.CodeFileBuffer.Previous())
     
     def LoadNext(self):
-        self.CodeFile = cPickle.loads(self.CodeFileBuffer.Next())
+        self.CodeFile = self.cPickle_loads(self.CodeFileBuffer.Next())
     
     def GetBufferState(self):
         first = self.CodeFileBuffer.IsFirst() and not self.Buffering
--- a/c_ext/c_ext.py	Sun May 12 23:32:30 2013 +0200
+++ b/c_ext/c_ext.py	Mon May 13 14:31:23 2013 +0200
@@ -1,3 +1,4 @@
+
 import os
 
 from CFileEditor import CFileEditor
@@ -14,8 +15,19 @@
       </xsd:element>
     </xsd:schema>
     """
+    CODEFILE_NAME = "CFile"
+    SECTIONS_NAMES = [
+        "includes",
+        "globals",
+        "initFunction",
+        "cleanUpFunction",
+        "retrieveFunction",
+        "publishFunction"]
     EditorType = CFileEditor
     
+    def GenerateClassesFromXSDstring(self, xsd_string):
+        return GenerateClassesFromXSDstring(xsd_string)
+    
     def GetIconName(self):
         return "Cfile"
 
--- a/code_file.xsd	Sun May 12 23:32:30 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<xsd:schema targetNamespace="code_file.xsd" 
-            xmlns:codefile="code_file.xsd" 
-            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
-            elementFormDefault="qualified" 
-            attributeFormDefault="unqualified">
-
-  <xsd:element name="CodeFile">
-    <xsd:complexType>
-      <xsd:sequence>
-        <xsd:element name="includes" type="codefile:CodeText"/>
-        <xsd:element name="variables">
-          <xsd:complexType>
-            <xsd:sequence>
-              <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
-                <xsd:complexType>
-                  <xsd:attribute name="name" type="xsd:string" use="required"/>
-                  <xsd:attribute name="type" type="xsd:string" use="required"/>
-                  <xsd:attribute name="class" use="optional">
-                    <xsd:simpleType>
-                      <xsd:restriction base="xsd:string">
-                        <xsd:enumeration value="input"/>
-                        <xsd:enumeration value="memory"/>
-                        <xsd:enumeration value="output"/>
-                      </xsd:restriction>
-                    </xsd:simpleType>
-                  </xsd:attribute>
-                  <xsd:attribute name="initial" type="xsd:string" use="optional" default=""/>
-                </xsd:complexType>
-              </xsd:element>
-            </xsd:sequence>
-          </xsd:complexType>
-        </xsd:element>
-        <xsd:element name="globals" type="codefile:CodeText"/>
-        <xsd:element name="initFunction" type="codefile:CodeText"/>
-        <xsd:element name="cleanUpFunction" type="codefile:CodeText"/>
-        <xsd:element name="retrieveFunction" type="codefile:CodeText"/>
-        <xsd:element name="publishFunction" type="codefile:CodeText"/>
-      </xsd:sequence>
-    </xsd:complexType>
-  </xsd:element>
-  <xsd:complexType name="CodeText">
-    <xsd:annotation>
-      <xsd:documentation>Formatted text according to parts of XHTML 1.1</xsd:documentation>
-    </xsd:annotation>
-    <xsd:sequence>
-      <xsd:any namespace="http://www.w3.org/1999/xhtml" processContents="lax"/>
-    </xsd:sequence>
-  </xsd:complexType>
-</xsd:schema>
--- a/editors/CodeFileEditor.py	Sun May 12 23:32:30 2013 +0200
+++ b/editors/CodeFileEditor.py	Mon May 13 14:31:23 2013 +0200
@@ -12,9 +12,6 @@
 from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS
 from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
 
-SECTIONS_NAMES = ["Includes", "Globals", "Init",
-                  "CleanUp", "Retrieve", "Publish"]
-
 [STC_CODE_ERROR, STC_CODE_SEARCH_RESULT, 
  STC_CODE_SECTION] = range(15, 18)
 
@@ -120,7 +117,7 @@
         self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
         
         self.SectionsComments = {}
-        for section in SECTIONS_NAMES:
+        for section in self.Controler.SECTIONS_NAMES:
             section_comment = " %s section " % (section)
             len_headers = 78 - len(section_comment)
             section_comment = self.COMMENT_HEADER * (len_headers / 2) + \
@@ -131,10 +128,11 @@
                  "comment": section_comment,
             }
             
-        for i, section in enumerate(SECTIONS_NAMES):
+        for i, section in enumerate(self.Controler.SECTIONS_NAMES):
             section_infos = self.SectionsComments[section]
-            if i + 1 < len(SECTIONS_NAMES):
-                section_end = self.SectionsComments[SECTIONS_NAMES[i + 1]]["comment"]
+            if i + 1 < len(self.Controler.SECTIONS_NAMES):
+                section_end = self.SectionsComments[
+                    self.Controler.SECTIONS_NAMES[i + 1]]["comment"]
             else:
                 section_end = "$"
             section_infos["pattern"] = re.compile(
@@ -203,7 +201,7 @@
     def GetCodeText(self):
         parts = self.Controler.GetTextParts()
         text = ""
-        for section in SECTIONS_NAMES:
+        for section in self.Controler.SECTIONS_NAMES:
             section_comments = self.SectionsComments[section]
             text += section_comments["comment"]
             if parts[section] == "":
@@ -246,7 +244,7 @@
         for line in xrange(self.GetLineCount()):
             self.SetLineState(line, 0)
         
-        for section in SECTIONS_NAMES:
+        for section in self.Controler.SECTIONS_NAMES:
             section_comments = self.SectionsComments[section]
             start_pos = text.find(section_comments["comment"])
             end_pos = start_pos + len(section_comments["comment"])
@@ -263,7 +261,7 @@
     def RefreshModel(self):
         text = self.GetText()
         parts = {}
-        for section in SECTIONS_NAMES:
+        for section in self.Controler.SECTIONS_NAMES:
             section_comments = self.SectionsComments[section]
             result = section_comments["pattern"].search(text)
             if result is not None:
@@ -276,14 +274,26 @@
         if self.CallTipActive():
             self.CallTipCancel()
         key = event.GetKeyCode()
-        current_pos = self.GetSelection()[0]
-        
+        current_pos = self.GetCurrentPos()
+        selected = self.GetSelection()
+        text_selected = selected[0] != selected[1]
+        
+        # Disable to type any character in section header lines
         if (self.GetLineState(self.LineFromPosition(current_pos)) and
+            not text_selected and
             key not in NAVIGATION_KEYS + [
                 wx.WXK_RETURN,
                 wx.WXK_NUMPAD_ENTER]):
             return
         
+        # Disable to delete line between code and header lines
+        elif (self.GetCurLine()[0].strip() != "" and not text_selected and
+              (key == wx.WXK_BACK and
+               self.GetLineState(self.LineFromPosition(max(0, current_pos - 1))) or
+               key in [wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE] and
+               self.GetLineState(self.LineFromPosition(min(len(self.GetText()), current_pos + 1))))):
+            return
+        
         elif key == 32 and event.ControlDown():
             pos = self.GetCurrentPos()
 
@@ -435,6 +445,7 @@
     
     def Copy(self):
         self.CmdKeyExecute(wx.stc.STC_CMD_COPY)
+        self.ParentWindow.RefreshEditMenu()
     
     def Paste(self):
         self.ResetBuffer()
--- a/py_ext/PythonFileCTNMixin.py	Sun May 12 23:32:30 2013 +0200
+++ b/py_ext/PythonFileCTNMixin.py	Mon May 13 14:31:23 2013 +0200
@@ -3,7 +3,7 @@
 from PythonEditor import PythonEditor
 
 from xml.dom import minidom
-from xmlclass import *
+from xmlclass import GenerateClassesFromXSD
 import cPickle
 
 from CodeFileTreeNode import CodeFile
@@ -12,6 +12,13 @@
 
 class PythonFileCTNMixin(CodeFile):
     
+    CODEFILE_NAME = "PyFile"
+    SECTIONS_NAMES = [
+        "globals",
+        "init",
+        "cleanup",
+        "start",
+        "stop"]
     EditorType = PythonEditor
     
     def __init__(self):
@@ -39,53 +46,35 @@
     def PythonFileName(self):
         return os.path.join(self.CTNPath(), "py_ext.xml")
 
-    def GetPythonCode(self):
-        current_location = self.GetCurrentLocation()
-        # define a unique name for the generated C file
-        location_str = "_".join(map(str, current_location))
+    def GetSectionsCode(self):
         
-        text = "## Code generated by Beremiz python mixin confnode\n\n"
-        
-        # Adding includes
-        text += "## User includes\n"
-        text += self.CodeFile.includes.gettext().strip()
-        text += "\n"
-        
-        # Adding variables
-        text += "## User variables reference\n"
+        # Generate Beremiz python runtime variables code
         config = self.GetCTRoot().GetProjectConfigNames()[0]
+        variables_str = ""
         for variable in self.CodeFile.variables.variable:
             global_name = "%s_%s" % (config.upper(), variable.getname().upper())
-            text += "# global_var:%s python_var:%s type:%s initial:%s\n" % (
+            variables_str += "# global_var:%s python_var:%s type:%s initial:%s\n" % (
                 global_name,
                 variable.getname(),
                 variable.gettype(),
                 str(variable.getinitial()))
-        text += "\n"
         
-        # Adding user global variables and routines
-        text += "## User internal user variables and routines\n"
-        text += self.CodeFile.globals.gettext().strip()
-        text += "\n"
+        sections_code = {
+            "variables": variables_str,
+            "globals": self.CodeFile.globals.gettext().strip()
+        }
         
-        # Adding Beremiz confnode functions
-        text += "## Beremiz confnode functions\n"
-        for func, args, return_code, code_object in [
-            ("__init_", "*args, **kwargs", 
-             "return 0", self.CodeFile.initFunction),
-            ("__cleanup_", "", "", self.CodeFile.cleanUpFunction),
-            ("__retrieve_", "", "", self.CodeFile.retrieveFunction),
-            ("__publish_", "", "", self.CodeFile.publishFunction),]:
-            text += "def %s%s(%s):\n" % (func, location_str, args)
-            lines = code_object.gettext().strip().splitlines()
-            if len(lines) > 0 or return_code != "":
-                for line in lines:
-                    text += "    " + line + "\n"
-                if return_code != "":
-                    text += "    " + return_code + "\n"
-                text += "\n"
-            else:
-                text += "    pass\n\n"
-        
-        return text
+        # Generate Beremiz python runtime functions code
+        for section in self.SECTIONS_NAMES:
+            if section != "globals":
+                code_object = getattr(self.CodeFile, section)
+                section_str = ""
+                lines = code_object.gettext().strip().splitlines()
+                if len(lines) > 0:
+                    for line in lines:
+                        section_str += "    " + line + "\n"
+                    section_str += "\n"
+                sections_code[section] = section_str
+            
+        return sections_code
 
--- a/py_ext/py_ext.py	Sun May 12 23:32:30 2013 +0200
+++ b/py_ext/py_ext.py	Mon May 13 14:31:23 2013 +0200
@@ -38,9 +38,35 @@
         # define a unique name for the generated C file
         location_str = "_".join(map(lambda x:str(x), current_location))
         
+        sections_code = self.GetSectionsCode()
+        
+        text = "## Code generated by Beremiz python mixin confnode\n\n"
+        
+        # Adding variables
+        text += "## User variables reference\n"
+        text += sections_code["variables"]
+        text += "\n"
+        
+        # Adding user global variables and routines
+        text += "## User internal user variables and routines\n"
+        text += sections_code["globals"]
+        text += "\n"
+        
+        # Adding Beremiz python runtime functions
+        text += "## Beremiz python runtime functions\n"
+        for section in self.SECTIONS_NAMES:
+            if section != "globals":
+                code_object = getattr(self.CodeFile, section)
+                text += "def _runtime_%s_%s():\n" % (location_str, section)
+                section_code = sections_code.get(section)
+                if section_code:
+                    text += section_code
+                else:
+                    text += "    pass\n\n"
+        
         runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str)
         runtimefile = open(runtimefile_path, 'w')
-        runtimefile.write(self.GetPythonCode())
+        runtimefile.write(text)
         runtimefile.close()
         
         return [], "", False, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))
--- a/wxglade_hmi/wxglade_hmi.py	Sun May 12 23:32:30 2013 +0200
+++ b/wxglade_hmi/wxglade_hmi.py	Mon May 13 14:31:23 2013 +0200
@@ -76,33 +76,59 @@
             runtimefile.write(hmipyfile.read())
             hmipyfile.close()
         
-        runtimefile.write(self.GetPythonCode())
-        runtimefile.write("""
-%(declare)s
-
-def _runtime_%(location)s_start():
-    global %(global)s
-    
-    def OnCloseFrame(evt):
-        wx.MessageBox(_("Please stop PLC to close"))
-    
-    %(init)s
-    
-def _runtime_%(location)s_stop():
-    global %(global)s
-    
-    %(cleanup)s
-
-""" % {"location": location_str,
-       "declare": "\n".join(map(lambda x:"%s = None" % x, hmi_frames.keys())),
-       "global": ",".join(hmi_frames.keys()),
-       "init": "\n".join(map(lambda x: """
+        sections_code = self.GetSectionsCode()
+        
+        # Adding variables
+        runtimefile.write("## User variables reference\n" +
+                          sections_code["variables"] + "\n")
+        
+        # Adding user global variables and routines
+        runtimefile.write("## User internal user variables and routines\n" +
+                          sections_code["globals"] + "\n")
+        
+        for section in ["init", "cleanup"]:
+            if not sections_code[section]:
+                sections_code = "    pass"
+        
+        sections_code.update({
+            "location": location_str,
+            "declare_hmi": "\n".join(map(lambda x:"%s = None" % x, hmi_frames.keys())),
+            "global_hmi": ",".join(hmi_frames.keys()),
+            "init_hmi": "\n".join(map(lambda x: """
     %(name)s = %(class)s(None)
     %(name)s.Bind(wx.EVT_CLOSE, OnCloseFrame)
     %(name)s.Show()
 """ % {"name": x[0], "class": x[1]},
                              hmi_frames.items())),
-       "cleanup": "\n    ".join(map(lambda x:"if %s is not None: %s.Destroy()" % (x,x), hmi_frames.keys()))})
+            "cleanup_hmi": "\n    ".join(map(lambda x:"if %s is not None: %s.Destroy()" % (x,x), hmi_frames.keys()))})
+        
+        runtimefile.write("""
+%(declare_hmi)s
+
+def _runtime_%(location)s_init():
+%(init)s
+
+def _runtime_%(location)s_cleanup():
+%(cleanup)s
+
+def _runtime_%(location)s_start():
+    global %(global_hmi)s
+    
+%(start)s
+
+    def OnCloseFrame(evt):
+        wx.MessageBox(_("Please stop PLC to close"))
+    
+    %(init_hmi)s
+    
+def _runtime_%(location)s_stop():
+    global %(global_hmi)s
+    
+    %(cleanup_hmi)s
+
+%(stop)s
+
+""" % sections_code)
         runtimefile.close()
         
         return [], "", False, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))
--- a/xmlclass/__init__.py	Sun May 12 23:32:30 2013 +0200
+++ b/xmlclass/__init__.py	Mon May 13 14:31:23 2013 +0200
@@ -24,5 +24,5 @@
 
 # Package initialisation
 
-from xmlclass import ClassFactory, GenerateClasses, GetAttributeValue, time_model, CreateNode, NodeSetAttr, NodeRenameAttr
+from xmlclass import ClassFactory, GenerateClasses, GetAttributeValue, time_model, CreateNode, NodeSetAttr, NodeRenameAttr, UpdateXMLClassGlobals
 from xsdschema import XSDClassFactory, GenerateClassesFromXSD, GenerateClassesFromXSDstring
--- a/xmlclass/xmlclass.py	Sun May 12 23:32:30 2013 +0200
+++ b/xmlclass/xmlclass.py	Mon May 13 14:31:23 2013 +0200
@@ -1857,9 +1857,11 @@
 def GenerateClasses(factory):
     ComputedClasses = factory.CreateClasses()
     if factory.FileName is not None and len(ComputedClasses) == 1:
-        globals().update(ComputedClasses[factory.FileName])
+        UpdateXMLClassGlobals(ComputedClasses[factory.FileName])
         return ComputedClasses[factory.FileName]
     else:
-        globals().update(ComputedClasses)
+        UpdateXMLClassGlobals(ComputedClasses)
         return ComputedClasses
 
+def UpdateXMLClassGlobals(classes):
+    globals().update(classes)