[5916] | 1 | #!/usr/bin/env python |
---|
[3620] | 2 | ### =========================================================================== |
---|
| 3 | ### |
---|
| 4 | ### Modifies or add an element in an XML file |
---|
| 5 | ### |
---|
| 6 | ### =========================================================================== |
---|
| 7 | ## |
---|
| 8 | ## Warning, to install, configure, run, use any of Olivier Marti's |
---|
| 9 | ## software or to read the associated documentation you'll need at least |
---|
| 10 | ## one (1) brain in a reasonably working order. Lack of this implement |
---|
| 11 | ## will void any warranties (either express or implied). |
---|
| 12 | ## O. Marti assumes no responsability for errors, omissions, |
---|
| 13 | ## data loss, or any other consequences caused directly or indirectly by |
---|
| 14 | ## the usage of his software by incorrectly or partially configured |
---|
| 15 | ## personal. |
---|
| 16 | ## |
---|
[3623] | 17 | ## SVN information |
---|
[3665] | 18 | __Author__ = "$Author$" |
---|
| 19 | __Date__ = "$Date$" |
---|
| 20 | __Revision__ = "$Revision$" |
---|
| 21 | __Id__ = "$Id$" |
---|
[3633] | 22 | __HeadURL = "$HeadURL$" |
---|
[3623] | 23 | |
---|
[6091] | 24 | # python update_xml.py -i ~/Unix/TOOLS/MOSAIX/iodef_atm_to_oce.xml -o essai.xml -n 'context[@id="interpol_read"]/file_definition/file[@id="file_src"]/field[@id="mask_src"]' -k name -v Bidon |
---|
| 25 | # python update_xml.py -i ~/Unix/TOOLS/MOSAIX/iodef_atm_to_oce.xml -d -o essai.xml -n 'context[@id="interpol_run"]/file_definition/file[@id="dia"]/variable[@name="title"]' -t "SRC mask interpolated to DST" |
---|
| 26 | # python update_xml.py -i ~/Unix/TOOLS/MOSAIX/iodef_atm_to_oce.xml -o essai.xml -c InFile.txt |
---|
| 27 | |
---|
[3620] | 28 | # Tested with python/2.7.12 and python/3.6.4 |
---|
| 29 | # |
---|
| 30 | import xml.etree.ElementTree |
---|
[5916] | 31 | import argparse, sys, textwrap |
---|
[3620] | 32 | |
---|
[3671] | 33 | # Check version of Python |
---|
| 34 | Version = sys.version_info |
---|
| 35 | if Version < (2,7,0) : |
---|
[3620] | 36 | sys.stderr.write ( "You need python 2.7 or later to run this script\n" ) |
---|
[3671] | 37 | sys.stderr.write ( "Present version is: " + str(Version[0]) + "." + str(Version[1]) + "." + str(Version[2]) + "\n" ) |
---|
[3620] | 38 | sys.exit (1) |
---|
[6091] | 39 | |
---|
| 40 | ## ============================================================================ |
---|
| 41 | ## Needed functions |
---|
| 42 | |
---|
| 43 | def simplify_string_list (list_str) : |
---|
| 44 | '''Concatenate some elements of the list of strings when needed''' |
---|
| 45 | zlist = list_str.copy () ; list_new = [] |
---|
| 46 | while ( len (zlist) > 0 ) : |
---|
| 47 | arg = zlist.pop (0) |
---|
| 48 | if arg[0] == '"' : |
---|
| 49 | for arg2 in zlist.copy () : |
---|
| 50 | arg = arg + " " + arg2 |
---|
| 51 | zlist.pop (0) |
---|
| 52 | if arg2[-1] == '"' : break |
---|
| 53 | arg = arg.strip('"').strip("'") |
---|
| 54 | list_new.append (arg) |
---|
| 55 | return list_new |
---|
[3620] | 56 | |
---|
[6091] | 57 | def UpdateNode (iodef, Node, Text, Key, Value) : |
---|
| 58 | '''Update an xml node''' |
---|
| 59 | # Remove whitespaces at both ends |
---|
| 60 | Node = Node.rstrip().lstrip() |
---|
| 61 | |
---|
| 62 | ## Find node |
---|
| 63 | nodeList = iodef.findall (Node) |
---|
| 64 | |
---|
| 65 | ## Check that one and only one node is found |
---|
| 66 | if len (nodeList) == 0 : |
---|
| 67 | print ( "Error : node not found" ) |
---|
| 68 | print ( "Node : ", Node ) |
---|
| 69 | sys.exit (1) |
---|
| 70 | |
---|
| 71 | if len (nodeList) > 1 : |
---|
| 72 | print ( "Error : " + len (nodeList)+" occurences of node found" ) |
---|
| 73 | print ( "Node : ", Node ) |
---|
| 74 | sys.exit (2) |
---|
| 75 | |
---|
| 76 | ## Update element |
---|
| 77 | elem = nodeList[0] |
---|
| 78 | |
---|
| 79 | if Debug : |
---|
| 80 | print ( 'Node:', Node, ' -- Key:', Key, ' -- Value:', Value , ' -- Text:', Text ) |
---|
| 81 | |
---|
| 82 | if Text != None : |
---|
| 83 | if Debug : |
---|
| 84 | print ( 'Attributes of node: ' + str (elem.attrib) ) |
---|
| 85 | print ( 'Text : ' + str (elem.text) ) |
---|
| 86 | elem.text = Text |
---|
| 87 | |
---|
| 88 | if Key != None : |
---|
| 89 | # To do : check that Key exist (it is added if not : do we want that ?) |
---|
| 90 | if Debug : |
---|
| 91 | print ( 'Attributes of node: ' + str (elem.attrib) ) |
---|
| 92 | elem.attrib.update ( { Key:Value } ) |
---|
| 93 | |
---|
| 94 | return iodef |
---|
| 95 | |
---|
| 96 | ## ============================================================================ |
---|
| 97 | ## Main code |
---|
| 98 | |
---|
[5916] | 99 | # Creating a parser to read the command line arguments |
---|
| 100 | # The first step in using the argparse is creating an ArgumentParser object: |
---|
| 101 | parser = argparse.ArgumentParser (description = """ |
---|
[6091] | 102 | Examples with the modification on the command line : |
---|
[5916] | 103 | python %(prog)s -i iodef.xml -n 'context[@id="interpol_run"]/file_definition/file[@id="file_src"]/field[@id="mask_source"]' -k name -v maskutil_T |
---|
| 104 | python %(prog)s -i iodef.xml -n 'context[@id="interpol_run"]/file_definition/file[@id="dia"]/variable[@name="dest_grid"]' -t dstDomainType |
---|
[3620] | 105 | |
---|
[6091] | 106 | Usage with a command file : |
---|
| 107 | python %(prog)s -i iodef.xml -c commands.txt |
---|
| 108 | |
---|
| 109 | Syntax in the command file : removes the quote around the node description : |
---|
| 110 | -n context[@id="interpol_run"]/file_definition/file[@id="dia"]/variable[@name="dest_grid"] -t dstDomainType |
---|
| 111 | |
---|
| 112 | """ + "SVN : " + __Revision__, formatter_class=argparse.RawDescriptionHelpFormatter, epilog='-------- This is the end of the help message --------') |
---|
| 113 | |
---|
[5916] | 114 | # Adding arguments |
---|
[6091] | 115 | group1 = parser.add_mutually_exclusive_group (required=False) |
---|
[3620] | 116 | |
---|
[6091] | 117 | parser.add_argument ( '-i', '--input' , help="XML input file" , default='iodef.xml', type=str, metavar='<input_file>' ) |
---|
| 118 | parser.add_argument ( '-o', '--output' , help="XML output file" , default=None , type=str, metavar='<output_file>' ) |
---|
| 119 | parser.add_argument ( '-n', '--node' , help="XML node in Xpath syntax", default=None, type=str, metavar='<xml_node>') |
---|
| 120 | group1.add_argument ( '-k', '--key' , help="XML key to update" , default=None , type=str , metavar='<xml_key>' ) |
---|
| 121 | group1.add_argument ( '-t', '--text' , help="Will replace the 'text' part of the Xpath by <text>", default=None, type=str, metavar='<text>' ) |
---|
| 122 | parser.add_argument ( '-v', '--value' , help="New value for xml key ", default=None, type=str, metavar='<value>' ) |
---|
[5916] | 123 | parser.add_argument ( '-d', '--debug' , action="store_true", default=False ) |
---|
| 124 | parser.add_argument ( '-V', '--verbose', action="store_true", default=False ) |
---|
[6091] | 125 | parser.add_argument ( '-c', '--commandfile', help="file with list of command", default=None, type=str ) |
---|
[3620] | 126 | |
---|
[5916] | 127 | # Parse command line |
---|
[6091] | 128 | myargs = parser.parse_args () |
---|
| 129 | Verbose = myargs.verbose |
---|
| 130 | Debug = myargs.debug |
---|
[3620] | 131 | |
---|
[6091] | 132 | if Debug : print ( "Command line arguments : ", myargs ) |
---|
[3671] | 133 | |
---|
[6091] | 134 | FileCommand = myargs.commandfile |
---|
| 135 | FileIn = myargs.input |
---|
| 136 | FileOut = myargs.output |
---|
| 137 | Node = myargs.node |
---|
| 138 | Key = myargs.key |
---|
| 139 | Text = myargs.text |
---|
| 140 | Value = myargs.value |
---|
| 141 | |
---|
| 142 | if FileCommand != None : |
---|
| 143 | if ( Node != None or Key != None or Text != None or Value != None ) : |
---|
| 144 | print ('Error : when a command file is specified, options -k|--key, -n|--node and -v|--value a unused' ) |
---|
| 145 | exit (-2) |
---|
| 146 | |
---|
[3671] | 147 | if FileOut == None : FileOut = FileIn |
---|
[6091] | 148 | |
---|
[3620] | 149 | ## Get XML tree from input file |
---|
| 150 | iodef = xml.etree.ElementTree.parse ( FileIn ) |
---|
| 151 | |
---|
[6091] | 152 | if FileCommand == None : |
---|
| 153 | ## Only one node to modify |
---|
[3620] | 154 | |
---|
[6091] | 155 | # Error handling not dealed by argparse |
---|
| 156 | if Key != None and Value == None : |
---|
| 157 | print ( "Error. When -k|--key=<key> is specified, you must specify -v|--value=<xml_value>" ) |
---|
| 158 | sys.exit (-1) |
---|
[3620] | 159 | |
---|
[6091] | 160 | iodef = UpdateNode ( iodef, Node, Text, Key, Value) |
---|
[3620] | 161 | |
---|
[6091] | 162 | else : |
---|
| 163 | ## Read a list of modification commands in a command file |
---|
| 164 | fic = open (FileCommand, 'r') |
---|
| 165 | lignes = fic.readlines() |
---|
[3620] | 166 | |
---|
[6091] | 167 | for nn, ligne in enumerate (lignes) : |
---|
[3620] | 168 | |
---|
[6091] | 169 | ligne = ligne.strip() |
---|
| 170 | if ligne == '' : break # Skips blank lines |
---|
| 171 | if ligne[0] == '#' : break # Skips comment lines |
---|
| 172 | |
---|
| 173 | ligne = ligne.split('#')[0] # Remove trailing comments |
---|
| 174 | |
---|
| 175 | list_args = ligne.split() |
---|
| 176 | list_args = simplify_string_list ( list_args ) |
---|
| 177 | |
---|
| 178 | if Debug : |
---|
| 179 | print ( '{:3d} : '.format(nn+1), end='') |
---|
| 180 | print ( list_args ) |
---|
| 181 | myargs_ligne = parser.parse_args ( list_args ) |
---|
| 182 | |
---|
| 183 | Node = myargs_ligne.node |
---|
| 184 | Key = myargs_ligne.key |
---|
| 185 | Text = myargs_ligne.text |
---|
| 186 | Value = myargs_ligne.value |
---|
| 187 | |
---|
| 188 | if Key != None and Value == None : |
---|
| 189 | print ( "Error. When -k|--key=<key> is specified, you must specify -v|--value=<xml_value>" ) |
---|
| 190 | sys.exit (-1) |
---|
| 191 | UpdateNode ( iodef, Node, Text, Key, Value) |
---|
[3620] | 192 | |
---|
| 193 | ## Writes XML tree to file |
---|
| 194 | iodef.write ( FileOut ) |
---|
| 195 | |
---|
| 196 | ## This is the end |
---|
[3671] | 197 | sys.exit (0) |
---|
[3620] | 198 | |
---|
| 199 | ### =========================================================================== |
---|
| 200 | ### |
---|
| 201 | ### That's all folk's !!! |
---|
| 202 | ### |
---|
| 203 | ### =========================================================================== |
---|
| 204 | |
---|