Удобный способ обработки конфигурационных файлов в python?

#python #configuration

#python #конфигурация

Вопрос:

Я хочу написать программу, которая отправляет электронное письмо одному или нескольким указанным получателям при возникновении определенного события. Для этого мне нужно, чтобы пользователь записал параметры для почтового сервера в конфигурацию. Возможными значениями являются, например: адрес сервера, порты, ssl (true / false) и список желаемых получателей.

Какой самый удобный для пользователя способ сделать это?

Я мог бы, конечно, использовать файл python с правильными параметрами, и пользователь должен заполнить его, но я бы не счел это удобным для пользователя. Я также читал о модуле ‘config’ в python, но мне кажется, что он создан для самостоятельного создания конфигурационных файлов, а не для того, чтобы пользователи сами заполняли файлы.

Комментарии:

1. Вы должны проверить docs.python.org/library/configparser.html

2. Я не понимаю. Пользователю придется записывать параметры каждый раз, когда будет отправлено электронное письмо? Зачем в этом случае записывать параметры в конфигурационный файл? Разве данных, содержащихся в строке, созданной с параметрами, заданными пользователем, было бы недостаточно?

3. @eyquem Пользователь должен ввести данные один раз в конфигурационный файл. После этого программа всегда считывает его из файла.

Ответ №1:

Вы хотите сказать, что тот факт, что файл конфигурации должен быть действительным в Python, делает его недружественным? Похоже, что в файле есть строки, подобные:

  server = 'mail.domain.com'
 port = 25
  

…etc был бы достаточно интуитивно понятным, оставаясь при этом действительным Python. Однако, если вы не хотите, чтобы пользователь знал, что ему приходится заключать строки в кавычки, вы можете пойти по пути YAML. Я использую YAML в значительной степени исключительно для конфигурационных файлов и нахожу его очень интуитивным, и, я думаю, он также был бы интуитивно понятен для конечного пользователя (хотя для этого требуется сторонний модуль — PyYAML):

  server: mail.domain.com
 port: 25
  

После загрузки pyyaml это просто:

 >>> import yaml
>>> yaml.load("""a: 1
... b: foo
... """)
{'a': 1, 'b': 'foo'}
  

С файлом это тоже просто.

 >>> with open('myconfig.yaml', 'r') as cfile:
...    config = yaml.load(cfile)
... 
  

конфигурация теперь содержит все параметры.

Комментарии:

1. Спасибо за ваш ответ. Я боюсь, что python может быть неудобен для пользователя из-за необходимости заключать строки в кавычки (как вы упомянули) и из-за отступов. Yaml звучит великолепно, в Ubuntu даже есть пакет python-yaml для него.

2. yaml действительно великолепен, я часто использую его для файлов конфигурации

3. @Basil: Большинство людей могут без труда найти ключи «в кавычках» и сопоставить «апострофам». Для англоязычных пользователей это стандартно, и все они знают, как это сделать. Ваши пользователи не владеют английским языком?

4. @S.Лотт Я не хотел подразумевать, что пользователи не смогут это понять. Просто отсутствие необходимости заботиться о кавычках и отступах упрощает задачу.

5. @Basil: В файле конфигурации никогда не должно быть никаких отступов и только самые простые правила цитирования. Вы обнаружите, что соответствующее подмножество Python часто намного проще , чем все другие варианты синтаксиса.

Ответ №2:

Не имеет значения, насколько технически опытны ваши пользователи; вы можете рассчитывать на то, что они облажаются при редактировании текстового файла. (Они сохранят его не в том месте. Они будут использовать MS Word для редактирования текстового файла. Они будут делать опечатки.) Я предлагаю создать графический интерфейс, который проверяет входные данные и создает файл конфигурации в правильном формате и расположении. Простой графический интерфейс, созданный в Tkinter, вероятно, соответствовал бы вашим потребностям.

Комментарии:

1. Я согласен с вами. Но на данный момент программа доступна только из командной строки, и в таком виде она останется на некоторое время.

2. Вы все еще можете создать программу, которая запрашивает параметры конфигурации и сохраняет их корректно с помощью raw_input . Если вам необходимо, чтобы ваши пользователи редактировали файл напрямую, я бы согласился с yaml ответом joesy.

Ответ №3:

Я использовал ConfigParser. Он предназначен для чтения файлов в стиле .ini, которые имеют:

 [section]
option = value
  

Он довольно прост в использовании, а документация довольно легко читается. По сути, вы просто загружаете весь файл в объект ConfigParser:

 import ConfigParser    

config = ConfigParser.ConfigParser()
config.read('configfile.txt')
  

Затем вы можете убедиться, что пользователи ничего не перепутали, проверив параметры. Я делаю это с помощью списка:

 OPTIONS = 
    ['section,option,defaultvalue',
     .
     .
     .
    ]

for opt in OPTIONS:
    section,option,defaultval = opt.split(',')
    if not config.has_option(section,option):
        print "Missing option %s in section %s" % (option,section)
  

Получение значений также легко.

 val = config.get('section','option')
  

И я также написал функцию, которая создает пример конфигурационного файла, используя этот список ОПЦИЙ.

     new_config = ConfigParser.ConfigParser()
    for opt in OPTIONS:
        section,option,defaultval = opt.split(',')
        if not new_config.has_section(section):
            new_config.add_section(section)
        new_config.set(section, option, defaultval)
    with open("sample_configfile.txt", 'wb') as newconfigfile:
        new_config.write(newconfigfile)
    print "Generated file: sample_configfile.txt"
  

Ответ №4:

Каковы недостатки такого решения:

 ch = 'serveradress = %snport = %snssl = %s'

a = raw_input("Enter the server's address : ")

b = 'a'
bla = "nEnter the port : "
while not all(x.isdigit() for x in b):
    b = raw_input(bla)
    bla = "Take care: you must enter digits exclusivelyn"
           "  Re-enter the port (digits only) : "

c = ''
bla = "nChoose the ssl option (t or f) : "
while c not in ('t','f'):
    c = raw_input(bla)
    bla = "Take care: you must type f or t exclusivelyn"
           "  Re-choose the ssl option : "


with open('configfile.txt','w') as f:
    f.write(ch % (a,b,c))
  

.

PS

Я прочитал в сообщении jonesy’s, что значение в файле конфигурации, возможно, придется заключать в кавычки. Если это так, и вы хотите, чтобы пользователю не приходилось самому писать кавычки, вы просто добавляете

 a = a.join('""')
b = b.join('""')
c = c.join('""')
  

.

Редактировать

 ch = 'serveradress = %snport = %snssl = %s'

d = {0:('',
        "Enter the server's address : "),
     1:("Take care: you must enter digits exclusively",
        "Enter the port : "),
     2:("Take care: you must type f or t exclusively",
        "Choose the ssl option (t or f) : ")  }

def func(i,x):
    if x is None:
        return False
    if i==0:
        return True
    elif i==1:
        try:
            ess = int(x)
            return True
        except:
            return False
    elif i==2:
        if x in ('t','f'):
            return True
        else:
            return False


li = len(d)*[None]
L = range(len(d))

while True:

    for n in sorted(L):
        bla = d[n][1]
        val = None
        while not func(n,val):
            val = raw_input(bla)
            bla = 'n  '.join(d[n])
        li[n] = val.join('""')

    decision = ''
    disp = "n====== If you choose to process, =============="
            "n    the content of the file will be :nn" 
             ch % tuple(li) 
             "n==============================================="
             "nnDo you want to process (type y) or to correct (type c) : "
    while decision not in ('y','c'):
        decision = raw_input(disp)
        disp = "Do you want to process (type y) or to correct (type c) ? : "

    if decision=='y':
        break
    else:
        diag = False
        while not diag:
            vi = 'nWhat lines do you want to correct ?n'
                  'n'.join(str(j) ' - ' line for j,line in enumerate((ch % tuple(li)).splitlines()))
                  'nType numbers of lines belonging to range(0,' str(len(d)) ') separated by spaces) :n'
            to_modify = raw_input(vi)
            try:
                diag = all(int(entry) in xrange(len(d)) for entry in to_modify.split())
                L = [int(entry) for entry in to_modify.split()]
            except:
                diag = False


with open('configfile.txt','w') as f:
    f.write(ch % tuple(li))

print '-*-  Recording of the config file : done  -*-'
  

Комментарии:

1. Я думаю, что в целом это хорошая идея. Единственная проблема, которую я вижу в этом, заключается в том, что если вы допустите опечатку, вам придется вводить все заново. Но я думаю, что добавлю комментарии вроде вашего («.. вы должны вводить исключительно цифры») к файлу.

2. @Basil Ну, это можно исправить. Дайте мне немного времени, и я предложу вам улучшение.