13
\$\begingroup\$

I have to modify the number of points in this XML in order to test the performance of another program of mine. Here is an example of the XML I have to modify.

performance.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!-- ICCP Local Control Center Configuration -->
<!DOCTYPE LocalControlCenter SYSTEM "C:\Program Files\SISCO\IccpCfg\IccpCfg.dtd">
<LocalControlCenter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="C:\Program Files\SISCO\IccpCfg\IccpCfg.xsd">
    <Name>Osiris_Control_Center</Name>
    <MaxDsTs>10</MaxDsTs>
    <MaxDataSets>10</MaxDataSets>
    <MaxMmsMsgSize>32000</MaxMmsMsgSize>
    <Description>calling site</Description>
    <LocalObjects>
        <LocalDataValues Count="15">
            <Ldv>
                <Name>Osiris_Local_Data_0001</Name>
                <DataType>State</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0002</Name>
                <DataType>StateQ</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0003</Name>
                <DataType>StateQTimeTag</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0004</Name>
                <DataType>StateExtended</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0005</Name>
                <DataType>StateQTimeTagExtended</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0006</Name>
                <DataType>Real</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0007</Name>
                <DataType>RealQ</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0008</Name>
                <DataType>RealQTimeTag</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0009</Name>
                <DataType>RealExtended</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0010</Name>
                <DataType>RealQTimeTagExtended</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0011</Name>
                <DataType>Discrete</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0012</Name>
                <DataType>DiscreteQ</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0013</Name>
                <DataType>DiscreteQTimeTag</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0014</Name>
                <DataType>DiscreteExtended</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
            <Ldv>
                <Name>Osiris_Local_Data_0015</Name>
                <DataType>DiscreteQTimeTagExtended</DataType>
                <NormalSource>Telemetered</NormalSource>
            </Ldv>
        </LocalDataValues>
        <LocalDevices Count="1">
            <Ldev>
                <DeviceName>Osiris_Device</DeviceName>
                <DeviceType>Discrete</DeviceType>
                <Sbo>N</Sbo>
                <ChkBackId>0</ChkBackId>
                <SelTime>30</SelTime>
                <TagEn>N</TagEn>
            </Ldev>
        </LocalDevices>
        <LocalInfoMsgs Count="0">
        </LocalInfoMsgs>
    </LocalObjects>
    <RemoteControlCenters Count="1">
        <RemoteControlCenter>
            <Name>flcon1</Name>
            <Version>2000-08</Version>
            <Description>The site that Listens for the connect request</Description>
            <BilateralTable>
                <Name>Osiris_Bilateral_1</Name>
                <Id>1</Id>
                <LocalDomain>Osiris</LocalDomain>
                <RemoteDomain>flcon1</RemoteDomain>
                <ShortestInterval>1</ShortestInterval>
                <Blocks>1,2</Blocks>
            </BilateralTable>
            <Associations Count="1">
                <Association>
                    <Name>PrimaryLink</Name>
                    <LocalAr>Osiris</LocalAr>
                    <RemoteAr>flcon1</RemoteAr>
                    <ConnectRole>Called</ConnectRole>
                    <AssocRetryTime>10</AssocRetryTime>
                    <InitiateTimeout>30</InitiateTimeout>
                    <ConcludeTimeout>30</ConcludeTimeout>
                    <AssocHeartbeatTime>10</AssocHeartbeatTime>
                    <ServiceRole>Client</ServiceRole>
                    <ServiceRole>Server</ServiceRole>
                    <MaxMmsMsgSize>32000</MaxMmsMsgSize>
                    <MaxReqPend>5</MaxReqPend>
                    <MaxIndPend>5</MaxIndPend>
                    <MaxNest>5</MaxNest>
                </Association>
            </Associations>
            <ServerObjects>
                <NumVccDv>0</NumVccDv>
                <NumVccDev>0</NumVccDev>
                <NumVccInfoMsg>0</NumVccInfoMsg>
                <NumVccDs>0</NumVccDs>
                <NumIccDv>15</NumIccDv>
                <NumIccDev>0</NumIccDev>
                <NumIccInfoMsg>0</NumIccInfoMsg>
                <NumIccDs>0</NumIccDs>
                <ServerDataValues Count="15">
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0001</ObjName>
                        <DataType>State</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0002</ObjName>
                        <DataType>StateQ</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>              
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0003</ObjName>
                        <DataType>StateQTimeTag</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0004</ObjName>
                        <DataType>StateExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0005</ObjName>
                        <DataType>StateQTimeTagExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0006</ObjName>
                        <DataType>Real</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0007</ObjName>
                        <DataType>RealQ</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0008</ObjName>
                        <DataType>RealQTimeTag</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0009</ObjName>
                        <DataType>RealExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0010</ObjName>
                        <DataType>RealQTimeTagExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0011</ObjName>
                        <DataType>Discrete</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0012</ObjName>
                        <DataType>DiscreteQ</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0013</ObjName>
                        <DataType>DiscreteQTimeTag</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0014</ObjName>
                        <DataType>DiscreteExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                    <Sdv>
                        <ObjName Scope="ICC">Osiris_Local_Data_0015</ObjName>
                        <DataType>DiscreteQTimeTagExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Sdv>
                </ServerDataValues>
                <ServerDevices Count="0">
                </ServerDevices>
                <ServerInfoMsgs Count="0">
                </ServerInfoMsgs>
                <ServerDataSets Count="0">
                </ServerDataSets>
            </ServerObjects>
            <ClientObjects>
                <NumVccDv>0</NumVccDv>
                <NumVccDev>0</NumVccDev>
                <NumVccInfoMsg>0</NumVccInfoMsg>
                <NumIccDv>15</NumIccDv>
                <NumIccDev>0</NumIccDev>
                <NumIccInfoMsg>0</NumIccInfoMsg>
                <NumDs>1</NumDs>
                <NumDsTs>1</NumDsTs>
                <ClientDataValues Count="15">
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0001</ObjName>
                        <DataType>State</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0002</ObjName>
                        <DataType>StateQ</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>              
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0003</ObjName>
                        <DataType>StateQTimeTag</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0004</ObjName>
                        <DataType>StateExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0005</ObjName>
                        <DataType>StateQTimeTagExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0006</ObjName>
                        <DataType>Real</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0007</ObjName>
                        <DataType>RealQ</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0008</ObjName>
                        <DataType>RealQTimeTag</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0009</ObjName>
                        <DataType>RealExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0010</ObjName>
                        <DataType>RealQTimeTagExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0011</ObjName>
                        <DataType>Discrete</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0012</ObjName>
                        <DataType>DiscreteQ</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0013</ObjName>
                        <DataType>DiscreteQTimeTag</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0014</ObjName>
                        <DataType>DiscreteExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                    <Cdv>
                        <ObjName Scope="ICC">Osiris_Test_Data_0015</ObjName>
                        <DataType>DiscreteQTimeTagExtended</DataType>
                        <ReadOnly>Y</ReadOnly>
                    </Cdv>
                </ClientDataValues>
                <ClientDevices Count="0">
                </ClientDevices>
                <ClientInfoMsgs Count="0">
                </ClientInfoMsgs>
                <ClientDataSets Count="1">
                    <Cds>
                        <Name>Test_Set</Name>
                        <Transfer_Set_Name>Y</Transfer_Set_Name>
                        <Transfer_Set_Time_Stamp>Y</Transfer_Set_Time_Stamp>
                        <DSConditions_Detected>Y</DSConditions_Detected>
                        <Event_Code_Detected>Y</Event_Code_Detected>
                        <CdsVars Count="15">
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0001"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0002"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0003"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0004"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0005"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0006"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0007"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0008"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0009"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0010"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0011"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0012"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0013"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0014"/>
                            <CdsVar Scope="ICC" Type="Dv" Name="Osiris_Test_Data_0015"/>
                        </CdsVars>
                    </Cds>
                </ClientDataSets>
                <ClientDataSetTransferSets Count="1">
                    <Cdsts>
                        <DsName>Test_Set</DsName>
                        <AssocName>PrimaryLink</AssocName>
                        <Interval>5</Interval>
                        <Rbe>N</Rbe>
                        <AllChangesReported>N</AllChangesReported>
                        <BufferTime>2</BufferTime>
                        <Integrity>0</Integrity>
                        <StartTime>0</StartTime>
                        <Critical>N</Critical>
                        <BlockData>N</BlockData>
                        <Tle>60</Tle>
                        <DsCondInterval>Y</DsCondInterval>
                        <DsCondIntegrity>N</DsCondIntegrity>
                        <DsCondChange>N</DsCondChange>
                        <DsCondOperator>N</DsCondOperator>
                        <DsCondExternal>N</DsCondExternal>
                    </Cdsts>
                </ClientDataSetTransferSets>
                <ClientDiscovery Enable="N">
                    <AssocName></AssocName>
                    <Execute>Never</Execute>
                    <GetNameList>N</GetNameList>
                    <RemoveMissing>Y</RemoveMissing>
                    <RemoveMistyped>Y</RemoveMistyped>
                    <RemoveReadError>Y</RemoveReadError>
                    <DbDeleteMissing>Y</DbDeleteMissing>
                    <DbCorrectMistyped>Y</DbCorrectMistyped>
                    <CfgGetVaa>N</CfgGetVaa>
                    <CfgRead>Y</CfgRead>
                    <NewAddDv>N</NewAddDv>
                    <DbAddNew>N</DbAddNew>
                    <NewReadOnly>N</NewReadOnly>
                    <NewGetVaa>N</NewGetVaa>
                    <NewRead>N</NewRead>
                    <DbAutoAccept>N</DbAutoAccept>
                    <WriteXml>N</WriteXml>
                </ClientDiscovery>
                <ClientAutoDsts Enable="N">
                    <AssocName>PrimaryLink</AssocName>
                    <AssignByType>N</AssignByType>
                    <AutoParam Enable="N" Type="All">
                        <AutoDs>
                            <MaxDstsPduSize>32000</MaxDstsPduSize>
                            <MaxDvPerDs>0</MaxDvPerDs>
                            <Conservative>Y</Conservative>
                            <Transfer_Set_Name>Y</Transfer_Set_Name>
                            <Transfer_Set_Time_Stamp>Y</Transfer_Set_Time_Stamp>
                            <DSConditions_Detected>Y</DSConditions_Detected>
                            <Event_Code_Detected>N</Event_Code_Detected>
                        </AutoDs>
                        <AutoDsts>
                            <StartTime>0</StartTime>
                            <Rbe>Y</Rbe>
                            <AllChangesReported>N</AllChangesReported>
                            <Critical>N</Critical>
                            <BlockData>N</BlockData>
                            <DsCondInterval>N</DsCondInterval>
                            <DsCondIntegrity>Y</DsCondIntegrity>
                            <DsCondChange>Y</DsCondChange>
                            <DsCondOperator>N</DsCondOperator>
                            <DsCondExternal>N</DsCondExternal>
                            <Interval>10</Interval>
                            <Integrity>30</Integrity>
                            <BufferTime>2</BufferTime>
                            <Tle>60</Tle>
                        </AutoDsts>
                    </AutoParam>
                    <AutoParam Enable="N" Type="Real">
                        <AutoDs>
                            <MaxDstsPduSize>32000</MaxDstsPduSize>
                            <MaxDvPerDs>0</MaxDvPerDs>
                            <Conservative>Y</Conservative>
                            <Transfer_Set_Name>Y</Transfer_Set_Name>
                            <Transfer_Set_Time_Stamp>Y</Transfer_Set_Time_Stamp>
                            <DSConditions_Detected>Y</DSConditions_Detected>
                            <Event_Code_Detected>N</Event_Code_Detected>
                        </AutoDs>
                        <AutoDsts>
                            <StartTime>0</StartTime>
                            <Rbe>Y</Rbe>
                            <AllChangesReported>N</AllChangesReported>
                            <Critical>N</Critical>
                            <BlockData>N</BlockData>
                            <DsCondInterval>N</DsCondInterval>
                            <DsCondIntegrity>Y</DsCondIntegrity>
                            <DsCondChange>Y</DsCondChange>
                            <DsCondOperator>N</DsCondOperator>
                            <DsCondExternal>N</DsCondExternal>
                            <Interval>10</Interval>
                            <Integrity>30</Integrity>
                            <BufferTime>2</BufferTime>
                            <Tle>60</Tle>
                        </AutoDsts>
                    </AutoParam>
                    <AutoParam Enable="N" Type="Discrete">
                        <AutoDs>
                            <MaxDstsPduSize>32000</MaxDstsPduSize>
                            <MaxDvPerDs>0</MaxDvPerDs>
                            <Conservative>Y</Conservative>
                            <Transfer_Set_Name>Y</Transfer_Set_Name>
                            <Transfer_Set_Time_Stamp>Y</Transfer_Set_Time_Stamp>
                            <DSConditions_Detected>Y</DSConditions_Detected>
                            <Event_Code_Detected>N</Event_Code_Detected>
                        </AutoDs>
                        <AutoDsts>
                            <StartTime>0</StartTime>
                            <Rbe>Y</Rbe>
                            <AllChangesReported>N</AllChangesReported>
                            <Critical>N</Critical>
                            <BlockData>N</BlockData>
                            <DsCondInterval>N</DsCondInterval>
                            <DsCondIntegrity>Y</DsCondIntegrity>
                            <DsCondChange>Y</DsCondChange>
                            <DsCondOperator>N</DsCondOperator>
                            <DsCondExternal>N</DsCondExternal>
                            <Interval>10</Interval>
                            <Integrity>30</Integrity>
                            <BufferTime>2</BufferTime>
                            <Tle>60</Tle>
                        </AutoDsts>
                    </AutoParam>
                    <AutoParam Enable="N" Type="State">
                        <AutoDs>
                            <MaxDstsPduSize>32000</MaxDstsPduSize>
                            <MaxDvPerDs>0</MaxDvPerDs>
                            <Conservative>Y</Conservative>
                            <Transfer_Set_Name>Y</Transfer_Set_Name>
                            <Transfer_Set_Time_Stamp>Y</Transfer_Set_Time_Stamp>
                            <DSConditions_Detected>Y</DSConditions_Detected>
                            <Event_Code_Detected>N</Event_Code_Detected>
                        </AutoDs>
                        <AutoDsts>
                            <StartTime>0</StartTime>
                            <Rbe>Y</Rbe>
                            <AllChangesReported>N</AllChangesReported>
                            <Critical>N</Critical>
                            <BlockData>N</BlockData>
                            <DsCondInterval>N</DsCondInterval>
                            <DsCondIntegrity>Y</DsCondIntegrity>
                            <DsCondChange>Y</DsCondChange>
                            <DsCondOperator>N</DsCondOperator>
                            <DsCondExternal>N</DsCondExternal>
                            <Interval>10</Interval>
                            <Integrity>30</Integrity>
                            <BufferTime>2</BufferTime>
                            <Tle>60</Tle>
                        </AutoDsts>
                    </AutoParam>
                </ClientAutoDsts>
            </ClientObjects>
        </RemoteControlCenter>
    </RemoteControlCenters>
</LocalControlCenter>

To streamline the setup, I created this Python script (despite me not knowing Python very well). It uses the standard Python library, and I would like to keep it that way.

setup.py:

from xml.etree import ElementTree as etree

def strip(elem):
    for elem in elem.getiterator():
        if(elem.text):
            elem.text = elem.text.strip()
        if(elem.tail):
            elem.tail = elem.tail.strip()

def removeBranch(tree, name):
    root = tree.getroot()
    parent_map = dict((c, p) for p in tree.getiterator() for c in p)
    for item in list(root.getiterator(name)):
        parent_map[item].remove(item)

def addLocalDataBranch(tree, num):
    parent = tree.find('.//LocalDataValues')
    parent.set('Count', str(num))
    for val in xrange(1, num+1): 
        item = etree.SubElement(parent, 'Ldv')
        name = etree.SubElement(item, 'Name')
        name.text = 'Osiris_Local_Data_' + str(val)
        type = etree.SubElement(item, 'DataType')
        type.text = 'RealQTimeTagExtended'
        source = etree.SubElement(item, 'NormalSource')
        source.text = 'Telemetered'

def addServerDataBranch(tree, num):
    obj = tree.find('.//ServerObjects/NumIccDv')
    obj.text = str(num)

    parent = tree.find('.//ServerDataValues')
    parent.set('Count', str(num))
    for val in xrange(1, num+1): 
        item = etree.SubElement(parent, 'Sdv')
        name = etree.SubElement(item, 'ObjName')
        name.set('Scope', 'ICC')
        name.text = 'Osiris_Local_Data_' + str(val)
        type = etree.SubElement(item, 'DataType')
        type.text = 'RealQTimeTagExtended'
        source = etree.SubElement(item, 'ReadOnly')
        source.text = 'Y'

def addClientDataBranch(tree, num):
    obj = tree.find('.//ClientObjects/NumIccDv')
    obj.text = str(num)

    parent = tree.find('.//ClientDataValues')
    parent.set('Count', str(num))
    for val in xrange(1, num+1): 
        item = etree.SubElement(parent, 'Cdv')
        name = etree.SubElement(item, 'ObjName')
        name.set('Scope', 'ICC')
        name.text = 'Osiris_Test_Data_' + str(val)
        type = etree.SubElement(item, 'DataType')
        type.text = 'RealQTimeTagExtended'
        source = etree.SubElement(item, 'ReadOnly')
        source.text = 'Y'

def addDataSetBranch(tree, num):
    parent = tree.find('.//CdsVars')
    parent.set('Count', str(num))
    for val in xrange(1, num+1): 
        item = etree.SubElement(parent, 'CdsVar')
        item.set('Scope', 'ICC')
        item.set('Type', 'Dv')
        item.set('Name', 'Osiris_Test_Data_' + str(val))

def editInterval(tree, num):
    parent = tree.find('.//Interval')
    parent.text = str(num)

if __name__ == '__main__':
    import sys
    if len(sys.argv[1:]) != 3:
        print('Usage: python setup.py [xml] [points] [time]')
        sys.exit(1)

    tree = etree.parse(sys.argv[1])
    removeBranch(tree, 'Ldv')
    removeBranch(tree, 'Sdv')
    removeBranch(tree, 'Cdv')
    removeBranch(tree, 'CdsVar')
    addLocalDataBranch(tree, int(sys.argv[2]))
    addServerDataBranch(tree, int(sys.argv[2]))
    addClientDataBranch(tree, int(sys.argv[2]))
    addDataSetBranch(tree, int(sys.argv[2]))
    editInterval(tree, int(sys.argv[3]))

    strip(tree.getroot())

    #print etree.tostring(tree.getroot(), 'utf-8')
    file = open(sys.argv[1], 'wb')
    file.write(etree.tostring(tree.getroot(), 'utf-8'))
    file.close()

As you can see, there is lots of repetition, and I'm sure there's more Python-ic ways to approach this problem. Any suggestions?

\$\endgroup\$
15
\$\begingroup\$

Low hanging fruit

Most of this will come down to reading PEP8, however I'll state them here anyway. From the top down:

  • If statements don't need brackets, so don't put them around them.
  • Functions at module level should have two new lines between them and other things. Rather than one as you have now.
  • You should use snake_case for functions and variables.
  • You can use a dictionary comprehension, rather than a generator comprehension fed to a dict constructor.
  • You should put a space both sides of operators, num + 1, the only exception to this is to show precedence.
  • Don't overwrite builtins type. Instead you can use type_ or a synonym.
  • Put all imports at the top of the script.
  • Use with rather than manually opening and closing.
  • Use a main function, this keeps things out of the global scope.

Removing repetitive code

At first I was doing some funky stuff to remove your duplicate code, but you can keep it simple. Just use generators, and make a few more functions. I'd make four more functions to reduce the repetition of your code.

You can also make set_obj_text which changes the attribute text. And so you can use this very small function:

def set_obj_text(node, num, obj):
    node.find(obj).text = str(num)

This will allow us to remove editInterval and instead use set_obj_text(tree, num, './/Interval').

After this is where we'll remove more of the duplicate code. By first getting a parent, we can then use a generator we can lazily get all the items. This is used in most of the other functions. This can be just be finding the node, and then the for loop with a yield of etree.SubElement.

def get_items(node, num, parent, item):
    parent_ = node.find(parent)
    parent_.set('Count', str(num))
    for i in xrange(1, num + 1):
        yield i, etree.SubElement(parent_, item)

This will allow us to change all the add*Branch functions. Allowing add_data_set_branch to be:

def add_data_set_branch(node, num):
    for val, item in get_items(node, num, './/CdsVars', 'CdsVar'):
        item.set('Scope', 'ICC')
        item.set('Type', 'Dv')
        item.set('Name', 'Osiris_Test_Data_' + str(val))

After this we can change all the add*Branch except add_data_set_branch to remove more duplicate code. This is as you mostly only change the nodes that you visit, being parent, item, name, name.text, source and the source.text. And so I'd make a function that takes these nodes names and does the repeated code. To make the return easier to use I'd use collections.namedtuple so that we can use ret.name rather than say ret[2]. This can result in:

def walk_branch(node, num, custom_values):
    v = custom_values.get
    for val, item in get_items(node, num, v('parent'), v('item')):
        name = etree.SubElement(item, v('name'))
        name.text = v('name_text') + str(val)
        type_ = etree.SubElement(item, 'DataType')
        type_.text = 'RealQTimeTagExtended'
        source = etree.SubElement(item, v('source'))
        source.text = v('source_text')
        yield branch(val, item, name, type_, source)

And allows us to change add_server_data_branch to:

def add_server_data_branch(node, num):
    set_obj_text(node, num, './/ServerObjects/NumIccDv')
    for ret in walk_branch(
            node, num, {
                'parent': './/ServerDataValues',
                'item': 'Sdv',
                'name': 'ObjName',
                'name_text': 'Osiris_Local_Data_',
                'source': 'ReadOnly',
                'source_text': 'Y',
            }):
        ret.name.set('Scope', 'ICC')

After this, I'd change how you call remove_branch. Instead of manually calling it a lot, you can use a for loop. And you can do the same for all the data*branchs.

for branch in ['Ldv', 'Sdv', 'Cdv', 'CdsVar']:
    remove_branch(tree, branch)
for fn in [add_local_data_branch, add_server_data_branch,
           add_client_data_branch, add_data_set_branch]:
    fn(tree, args.points)

This removes most of the duplicate code.


Finally I'd use argparse rather than sys.argv. This is as it checks the input for you so you can make it check if points is a number and it exit with a message about it. Rather than Python abruptly exiting with a ValueError. You can also add help information which will display if you use file.py -h, and it also automates your message 'Usage: python setup.py [xml] [points] [time]'. The only downside to this is you'll need to read more documentation to add more features to the argument input.


All the above changes resulted in me getting:

from xml.etree import ElementTree as etree
from collections import namedtuple
import argparse

branch = namedtuple('Branch', ['val', 'item', 'name', 'type', 'source'])


def set_obj_text(node, num, obj):
    node.find(obj).text = str(num)


def get_items(node, num, parent, item):
    parent_ = node.find(parent)
    parent_.set('Count', str(num))
    for i in xrange(1, num + 1):
        yield i, etree.SubElement(parent_, item)


def walk_branch(node, num, custom_values):
    v = custom_values.get
    for val, item in get_items(node, num, v('parent'), v('item')):
        name = etree.SubElement(item, v('name'))
        name.text = v('name_text') + str(val)
        type_ = etree.SubElement(item, 'DataType')
        type_.text = 'RealQTimeTagExtended'
        source = etree.SubElement(item, v('source'))
        source.text = v('source_text')
        yield branch(val, item, name, type_, source)


def strip(elem):
    for elem in elem.getiterator():
        if elem.text:
            elem.text = elem.text.strip()
        if elem.tail:
            elem.tail = elem.tail.strip()


def remove_branch(node, name):
    parent_map = {c: p for p in node.getiterator() for c in p}
    for item in node.getroot().getiterator(name):
        parent_map[item].remove(item)


def add_local_data_branch(node, num):
    # Consume generator
    for _ in walk_branch(
            node, num, {
                'parent': './/LocalDataValues',
                'item': 'Ldv',
                'name': 'Name',
                'name_text': 'Osiris_Local_Data_',
                'source': 'NormalSource',
                'source_text': 'Telemetered',
            }):
        pass


def add_server_data_branch(node, num):
    set_obj_text(node, num, './/ServerObjects/NumIccDv')
    for ret in walk_branch(
            node, num, {
                'parent': './/ServerDataValues',
                'item': 'Sdv',
                'name': 'ObjName',
                'name_text': 'Osiris_Local_Data_',
                'source': 'ReadOnly',
                'source_text': 'Y',
            }):
        ret.name.set('Scope', 'ICC')


def add_client_data_branch(node, num):
    set_obj_text(node, num, './/ClientObjects/NumIccDv')
    for ret in walk_branch(
            node, num, {
                'parent': './/ClientDataValues',
                'item': 'Cdv',
                'name': 'ObjName',
                'name_text': 'Osiris_Test_Data_',
                'source': 'ReadOnly',
                'source_text': 'Y',
            }):
        ret.name.set('Scope', 'ICC')


def add_data_set_branch(node, num):
    for val, item in get_items(node, num, './/CdsVars', 'CdsVar'):
        item.set('Scope', 'ICC')
        item.set('Type', 'Dv')
        item.set('Name', 'Osiris_Test_Data_' + str(val))


def get_arguments():
    parser = argparse.ArgumentParser(description='Convert XML to test performance.')
    parser.add_argument('xml', help='path to xml file.')
    parser.add_argument('points', type=int, help='')
    parser.add_argument('time', type=int, help='')
    return parser.parse_args()


def main():
    args = get_arguments()
    tree = etree.parse(args.xml)
    for branch in ['Ldv', 'Sdv', 'Cdv', 'CdsVar']:
        remove_branch(tree, branch)
    for fn in [add_local_data_branch, add_server_data_branch,
               add_client_data_branch, add_data_set_branch]:
        fn(tree, args.points)
    set_obj_text(tree, args.time, './/Interval')
    strip(tree.getroot())
    with open(args.xml, 'wb') as f:
        f.write(etree.tostring(tree.getroot(), 'utf-8'))


if __name__ == '__main__':
    main()
\$\endgroup\$
  • \$\begingroup\$ Bravo, great answer with great explanation \$\endgroup\$ – syb0rg Jul 6 '16 at 23:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.