source: TOOLS/MOSAIX/update_xml.py @ 6666

Last change on this file since 6666 was 6666, checked in by omamce, 9 months ago

O.M. : MOSAIX

Improved code with pylint analysis

  • Property svn:keywords set to Date Revision HeadURL Author Id
File size: 8.5 KB
RevLine 
[5916]1#!/usr/bin/env python
[3620]2### ===========================================================================
3###
4### Modifies or add an element in an XML file
5###
6### ===========================================================================
7##
[6190]8##  MOSAIX is under CeCILL_V2 licence. See "Licence_CeCILL_V2-en.txt"
9##  file for an english version of the licence and
10##  "Licence_CeCILL_V2-fr.txt" for a french version.
[3620]11##
[6190]12##  Permission is hereby granted, free of charge, to any person or
13##  organization obtaining a copy of the software and accompanying
14##  documentation covered by this license (the "Software") to use,
15##  reproduce, display, distribute, execute, and transmit the
16##  Software, and to prepare derivative works of the Software, and to
17##  permit third-parties to whom the Software is furnished to do so,
18##  all subject to the following:
19##
20##  Warning, to install, configure, run, use any of MOSAIX software or
21##  to read the associated documentation you'll need at least one (1)
22##  brain in a reasonably working order. Lack of this implement will
23##  void any warranties (either express or implied).  Authors assumes
24##  no responsability for errors, omissions, data loss, or any other
25##  consequences caused directly or indirectly by the usage of his
26##  software by incorrectly or partially configured
27##
[6666]28'''
29Python script used to perform on the fly
30editing of xml files. Used by `CreateWeights.bash`. More
31information with `python update_xml.py -h`.
32
[3623]33## SVN information
[6666]34Author   = "$Author$"
35Date     = "$Date$"
36Revision = "$Revision$"
37Id       = "$Id$"
38HeadURL  = "$HeadURL$"
39'''
[3623]40
[6666]41## SVN information
42__SVN__ = ({
43    'Author'   : "$Author$",
44    'Date'     : "$Date$",
45    'Revision' : "$Revision$",
46    'Id'       : "$Id$",
47    'HeadURL'  : "$HeadURL$",
48    })
49
[6093]50# 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=Bidon
[6091]51# 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"
52# python update_xml.py -i ~/Unix/TOOLS/MOSAIX/iodef_atm_to_oce.xml -o essai.xml -c InFile.txt
53
[3620]54# Tested with python/2.7.12 and python/3.6.4
55#
56import xml.etree.ElementTree
[6666]57import argparse, sys, shlex
58
[3671]59# Check version of Python
60Version = sys.version_info
[6091]61
62## ============================================================================
63## Needed functions
64
65def simplify_string_list (list_str) :
66    '''Concatenate some elements of the list of strings when needed'''
67    zlist = list_str.copy () ; list_new = []
[6666]68    while len (zlist) > 0 :
69        arg = zlist.pop (0)
[6091]70        if arg[0] == '"' :
71            for arg2 in zlist.copy () :
72                arg = arg + " " + arg2
73                zlist.pop (0)
74                if arg2[-1] == '"' : break
75        arg = arg.strip('"').strip("'")
76        list_new.append (arg)
77    return list_new
[6666]78
79def UpdateNode (piodef, pNode, pText=None, pKey=None) :
[6091]80    '''Update an xml node'''
81    # Remove whitespaces at both ends
[6666]82    zNode = pNode.rstrip().lstrip()
[6091]83
84    ## Find node
[6666]85    nodeList = piodef.findall (zNode)
[6091]86
87    ## Check that one and only one node is found
88    if len (nodeList) == 0 :
89        print ( "Error : node not found" )
[6666]90        print ( "Node  : ", zNode )
[6091]91        sys.exit (1)
[6666]92
[6091]93    if len (nodeList) > 1 :
[6093]94        print ( "Error : " + len (nodeList)+" occurences of node found in file" )
[6666]95        print ( "Node  : ", zNode )
[6091]96        sys.exit (2)
97
98    ## Update element
99    elem = nodeList[0]
100
[6666]101    if pText is not None :
102        if Verbose : print ( 'Node:', zNode, ' -- Text:', pText  )
[6091]103        if Debug :
104            print ( 'Attributes of node: ' + str (elem.attrib) )
105            print ( 'Text              : ' + str (elem.text)   )
[6666]106        elem.text = pText
[6091]107
[6666]108    if pKey is not None :
109
[6093]110        # Check the syntax
[6666]111        if not '=' in pKey :
[6093]112            print ( 'Key syntax error. Correct syntax is -k Key=Value' )
113            sys.exit (-1)
114        else :
[6666]115            KeyName, Value = pKey.split ('=')
116            if Verbose : print ( 'Node:', zNode, ' -- Key:', pKey )
[6093]117            # To do : check that KeyName exist (it is added if not : do we want that ?)
118            if Debug :
119                print ( 'Attributes of node: ' + str (elem.attrib) )
120            elem.attrib.update ( { KeyName:Value } )
[6091]121
[6666]122    return piodef
[6091]123
124## ============================================================================
125## Main code
126
[5916]127# Creating a parser to read the command line arguments
128# The first step in using the argparse is creating an ArgumentParser object:
129parser = argparse.ArgumentParser (description = """
[6666]130Examples with the modification on the command line :
[5916]131     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
132     python %(prog)s -i iodef.xml -n 'context[@id="interpol_run"]/file_definition/file[@id="dia"]/variable[@name="dest_grid"]'   -t dstDomainType
[3620]133
[6666]134Usage with a command file :
[6091]135     python %(prog)s -i iodef.xml -c commands.txt
[6666]136
137Syntax in the command file : removes the quote around the node description :
[6091]138-n context[@id="interpol_run"]/file_definition/file[@id="dia"]/variable[@name="dest_grid"]   -t dstDomainType
139
[6666]140   """ + "SVN : " + __SVN__['Revision'], formatter_class=argparse.RawDescriptionHelpFormatter, epilog='-------- This is the end of the help message --------')
[6091]141
[5916]142# Adding arguments
[6091]143group1 = parser.add_mutually_exclusive_group (required=False)
[3620]144
[6093]145parser.add_argument ( '-i', '--input'  , help="XML input file"          , default='iodef.xml', type=str, metavar='<input_file>'  )
146parser.add_argument ( '-o', '--output' , help="XML output file"         , default=None       , type=str, metavar='<output_file>' )
[6091]147parser.add_argument ( '-n', '--node'   , help="XML node in Xpath syntax", default=None, type=str, metavar='<xml_node>')
[6093]148group1.add_argument ( '-k', '--key'    , help="XML key to update and new value (-k <name>=<value>)", default=None, type=str, metavar='<xml_key>' )
[6091]149group1.add_argument ( '-t', '--text'   , help="Will replace the 'text' part of the Xpath by <text>", default=None, type=str, metavar='<text>' )
[6093]150parser.add_argument ( '-d', '--debug'  , help="Extensive debug prints", action="store_true", default=False )
151parser.add_argument ( '-v', '--verbose', help="Some verbosity"        , action="store_true", default=False )
[6091]152parser.add_argument ( '-c', '--commandfile', help="file with list of command", default=None, type=str )
[3620]153
[5916]154# Parse command line
[6091]155myargs  = parser.parse_args ()
156Verbose = myargs.verbose
157Debug   = myargs.debug
[3620]158
[6091]159if Debug : print ( "Command line arguments : ", myargs )
[3671]160
[6091]161FileCommand = myargs.commandfile
162FileIn      = myargs.input
163FileOut     = myargs.output
164Node        = myargs.node
165Key         = myargs.key
166Text        = myargs.text
[6666]167
168if FileCommand is not None :
169    if ( Node is not None or Key is not None or Text is not None ) :
[6093]170        print ('Error : when a command file is specified, options -k|--key, -n|--node are unused' )
[6091]171        exit (-2)
[6666]172
173if FileOut is None : FileOut = FileIn
174
[3620]175## Get XML tree from input file
176iodef = xml.etree.ElementTree.parse ( FileIn )
177
[6666]178if FileCommand is None :
[6091]179    ## Only one node to modify
[6093]180    iodef = UpdateNode (iodef, Node, Key, Text)
[3620]181
[6091]182else :
183    ## Read a list of modification commands in a command file
[6666]184    fic = open (FileCommand, 'r', encoding='utf-8')
185    lignes = fic.readlines ()
[3620]186
[6091]187    for nn, ligne in enumerate (lignes) :
[6093]188        ligne = ligne.strip ().split ('#')[0] # Remove leading and trailing blanks, and trailing comments
189        if Debug : print (nn+1, ':', type (ligne) , ':', len (ligne) , ':', ligne, ':')
[6666]190        if ligne == '' or ligne is None or len(ligne) == 0 or ligne[0] == '#' :
[6093]191            if Debug : print ('Skips blank or comment line')
[6666]192        else :
[6093]193            list_args = shlex.split (ligne)
[6666]194
[6093]195            if Debug :
196                print ( '{:3d} : '.format(nn+1), end='')
197                print ( list_args )
198            # Parse args line
199            myargs_ligne = parser.parse_args (list_args)
[3620]200
[6093]201            Node     = myargs_ligne.node
202            Key      = myargs_ligne.key
203            Text     = myargs_ligne.text
[6091]204
[6666]205            iodef = UpdateNode (iodef, Node, Text, Key)
206
[3620]207## Writes XML tree to file
208iodef.write ( FileOut )
209
210## This is the end
[6093]211if Debug : print ('This is the end')
212#sys.exit (0)
[6666]213
[3620]214### ===========================================================================
215###
216###                               That's all folk's !!!
217###
218### ===========================================================================
Note: See TracBrowser for help on using the repository browser.