Python: модуль ConfigParser для работы с файлами конфигураций, и скрипт редактирования файлов

Автор: | 31/12/2014
 

PythonНеобходимо было создать скрипт для редактирования файлов настроек приложения, что бы не изменять его вручную.

В скрипте используется модуль ConfigParser (ссылки в конце поста).

Можно вызывать либо с передачей опций (модуль argparce), либо – без опций.

В случае вызова с опциями – можно указать какой файл редактировать, иначе – будет использован файл по умолчанию (default=CONF_DIR + '/external.properties' в parser.add_argument).

Пример файла конфигурации:

[Connection settings]
jdbc.main.url = jdbc:oracle:thin:@host1
jdbc.main.username = USER
jdbc.main.password = pass

[Mail settings]
mail.smtp.host=127.0.0.1
mail.smtp.port=25
mail.smtp.on=false
mail.cronExpression=0 0/2 * ? * MON-SUN

[Import cron expressions]
gateKeeper.cronExpression=0 0 5 ? * MON-SUN
cleanup.compress.cronExpression=0 0 12 ? * SUN
send.mails.to.group.cronExpression = 0 0 12 ? * MON-SUN
exc_tckt_d.cronExpression=0 30 22 ? * MON-FRI

[Gate keeper settings]
gk.homedir=gate_keeper_report
sftp.useLocalFileIfAvailable=true
sftp.downloadBaseDir=~/download
sftp.maxRetryLimit=3

[TCP Report Extract Properties]
usePrecalculatedAggregation=true

[Other settings]
bean.datasource.query_log_wrapper=mainDataSourceWrapper
log.client.exception = false
time.to.keep.domain=7*12
time.to.keep.uncompress=1
dao.batch.size.max=30

И сам скрипт:

#!/usr/bin/env python

import os
import sys
import re

import logging
import time
import ConfigParser
import argparse

ENV = os.environ['ENV']
CONF_DIR = os.path.join(os.environ['HOME'], 'secure')
LOG =  os.path.join(os.environ['HOME'], 'logs/settings.log')

logging.basicConfig(format = '%(filename)s[LINE:%(lineno)d] - %(levelname)-3s [%(asctime)s] %(message)s ', filename=LOG, level=logging.DEBUG)

parser = argparse.ArgumentParser()

parser.add_argument('-f', '--file', default=CONF_DIR + '/external.properties', help='File to edit, default $HOME/secure/external.properties')
parser.add_argument('-s', '--section', help='Block name to edit option in')
parser.add_argument('-o', '--option', help='Option name to edit option in')
parser.add_argument('-v', '--value', help='New value, to set in option from --option')

RES = parser.parse_args()

print("nWorking on %s and using configuration directory %s.n" % (ENV,CONF_DIR))

logging.info('Setting edit started at %s' % (time.ctime()))

def answer(prompt, choice='Yes or no, please!'):

    '''Used for user communication, to confirm selected item.
       Return True if 'y' and False if 'n' entered.'''

    while True:
        result = raw_input(prompt)
        if result in ('y', 'Y', 'yes', 'Yes'):
            return True
        elif result in ('n', 'N', 'no', 'No'):
            return False
        else:
            print(choice)

def select_conf(path):

    '''Used to select configuration file from directory in CONF_DIR variable.
       Return filename without full path.'''

    while True:

        list = []

        for file in os.listdir(path):
            list.append(file)
            print('%s %s' % ((list.index(file)), file))

        try:
            res_file = int(raw_input('nPlease, select file to edit: '))
            print('You selected: %s' % list[res_file])
        except ValueError as e:
            print('Please, enter correct value: %sn' % e)
            sys.exit(2)

        if answer('Is it OK? [y/n] '):
            logging.info('Selected %s file to work with.' % list[res_file])
            return(list[res_file])
            break

def select_section(configfile):

    '''Use ConfigParser module to select sections, declared in [] blocks in file.
       File takes from select_conf() function.'''

    Config = ConfigParser.ConfigParser()
    file =  os.path.join(CONF_DIR, configfile)
    list = []

    try:
        Config.read(file)
    except ConfigParser.MissingSectionHeaderError as e:
        print(e)
        sys.exit(5)


    print('n')

    while True:

        for section in Config.sections():
            list.append(section)
            print('%s %s' % ((list.index(section)), section))

        try:
            res_section = int(raw_input('nPlease, select section to edit: '))
            print('You selected: %s' % list[res_section])
        except ValueError as e:
            print('Please, enter correct value: %sn' % e)
            sys.exit(3)

        if answer('Is it OK? [y/n] '):
            logging.info('Selected %s section to work with.' % list[res_section])
            return(list[res_section], file)
            break


def write_changes(file, section, option, value):

    file = os.path.join(CONF_DIR, file)
    print('Editing file: %s. Will set section: %s, option: %s, value: %s.n' % (file, section, option, value))
    Config = ConfigParser.ConfigParser()

    Config.read(file)
    Config.set(section, option, value)

    with open(file, 'r+') as c_file:
        Config.write(c_file)
        print('New value assigned successfully.')
        logging.info('New value assigned successfully. Finished at %s' % (time.ctime()))


def edit_option(*args):

    '''If script called without arguments:
       Use $section variable from select_section() function, to set section, to select options from,
       and $file variable from select_section() function, to set file, to be edited.

       First loop - to set new variable $res_option, where option to be edited saves;
       second loop - to set new varaible $new_value, to be saved in $section in $file.
       next. in second loop, with ConfigParcer - writing new $new_value in $file.

       If called with arguments:
       will set RES.file, RES.section, RES.option and RES.value from parser.parse_args()'''

    Config = ConfigParser.ConfigParser()

    if len(sys.argv) <= 1:

        section, file = select_section(select_conf(CONF_DIR))

        list = []

        Config.read(file)

        print('n')

        while True:

            for option in Config.options(section):
                list.append(option)
                print('%s %s' % ((list.index(option)), option))

            try:
                res_option = int(raw_input('nPlease, select option to edit: '))
                print('You selected: %s' % list[res_option])
            except ValueError as e:
                print('Please, enter correct value: %sn' % e)
                sys.exit(4)

            if answer('Is it OK? [y/n] '):
                logging.info('Selected %s option to work with.' % list[res_option])
                break

        while True:

            print('nCurrent value of %s is %s.n' % (list[res_option], Config.get(section, list[res_option])))
            logging.info('Current value of %s is %s.' % (list[res_option], Config.get(section, list[res_option])))

            new_value = raw_input('Please, set new value for %s: ' % list[res_option])
            print('Your new value for %s is %s.' % (list[res_option], new_value))
            logging.info('Entered new value %s.' % new_value)

            if answer('Is it OK? [y/n] '):
                write_changes(file, section, list[res_option], new_value)
                break
    else:
        logging.info('Running optional edit. File: %s; section: %s, option: %s, new value: %s' % (RES.file, RES.section, RES.option, RES.value))
        write_changes(RES.file, RES.section, RES.option, RES.value)


if __name__ == "__main__":

    try:
        edit_option(RES.section)
    except KeyboardInterrupt:
        logging.info('Exited by KeyboardInterrupt at %s.' % (time.ctime()))
        print('n')
        sys.exit(0)

Его работа, при вызове без опций:

$ ./setting.py

Working on DEV and using configuration directory /home/user/secure.

0 external.DEV.properties-NEW
1 external.properties_2014-11-29-12_44_bak
2 external.properties_2014-11-29-12_45_bak
3 external.properties-30_12_2014
4 test.property
5 external.properties
6 external.properties_encoding_bkp
7 secret.key

Please, select file to edit: 4
You selected: test.property
Is it OK? [y/n] y


0 Connection settings
1 Gate keeper settings
2 TCP Report Extract Properties
3 Mail settings
4 Import cron expressions
5 Other settings

Please, select section to edit: 0
You selected: Connection settings
Is it OK? [y/n] n
0 Connection settings
1 Gate keeper settings
2 TCP Report Extract Properties
3 Mail settings
4 Import cron expressions
5 Other settings

Please, select section to edit: 0
You selected: Connection settings
Is it OK? [y/n] y


0 jdbc.main.url
1 jdbc.main.password
2 jdbc.main.username

Please, select option to edit: 0
You selected: jdbc.main.url
Is it OK? [y/n] y

Current value of jdbc.main.url is host2.

Please, set new value for jdbc.main.url: host_new.com
Your new value for jdbc.main.url is host_new.com.
Is it OK? [y/n] y
New value assigned successfully.

Лог:

setting.py[LINE:19] - INFO [2014-12-30 14:00:43,339] Setting edit started at Tue Dec 30 14:00:43 2014
setting.py[LINE:58] - INFO [2014-12-30 14:00:47,948] Selected test.property file to work with.
setting.py[LINE:94] - INFO [2014-12-30 14:00:57,852] Selected Connection settings section to work with.
setting.py[LINE:130] - INFO [2014-12-30 14:01:02,164] Selected jdbc.main.url option to work with.
setting.py[LINE:136] - INFO [2014-12-30 14:01:02,164] Current value of jdbc.main.url is host2.
setting.py[LINE:139] - INFO [2014-12-30 14:01:09,916] Entered new value host_new.com.
setting.py[LINE:146] - INFO [2014-12-30 14:01:11,260] New value assigned successfully. Finished at Tue Dec 30 14:01:11 2014

Вызов с опциями, без указания файла, который редактировать:

$ ./setting.py -s "Connection settings" -o "jdbc.main.url" -v "besturlever.db.com"

Working on DEV and using configuration directory /home/user/secure.

Editing file: /home/user/secure/external.properties, section: Connection settings, option: jdbc.main.url, value: besturlever.db.com.

New value assigned successfully.

И с указанием файла:

$ ./setting.py -f "test.property" -s "Connection settings" -o "jdbc.main.url" -v "besturlever.db.com"

Working on DEV and using configuration directory /home/user/secure.

Editing file: /home/user/secure/test.property. Will set section: Connection settings, option: jdbc.main.url, value: besturlever.db.com.

New value assigned successfully.

Ссылки по модулю ConfigParser

https://docs.python.org

https://wiki.python.org

http://www.pythonforbeginners.com