【问题标题】:Userfriendly way of handling config files in python?在 python 中处理配置文件的用户友好方式?
【发布时间】:2011-08-24 05:15:23
【问题描述】:

我想编写一个程序,当某个事件发生时,它会向一个或多个指定的收件人发送电子邮件。为此,我需要用户将邮件服务器的参数写入配置。可能的值例如:服务器地址、端口、ssl(true/false) 和所需收件人列表。

什么是用户友好/最佳实践的方法?

我当然可以使用具有正确参数的 python 文件,并且用户必须填写它,但我不认为这种用户友好。我还阅读了 python 中的“配置”模块,但在我看来,它是为自己创建配置文件而设计的,而不是让用户自己填写文件。

【问题讨论】:

  • 我不明白。每次发送电子邮件时,用户都必须编写参数?为什么在这种情况下将参数记录在配置文件中?用用户给定的参数创建的字符串中包含的数据还不够吗?
  • @eyquem 用户应该在配置文件中输入一次数据。之后,程序总是从文件中读取它。

标签: python configuration


【解决方案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)
... 

config 现在包含所有参数。

【讨论】:

  • 感谢您的回答。我担心 python 可能不是用户友好的,因为需要引用字符串(正如你提到的)和缩进。 Yaml 听起来很棒,在 ubuntu 中甚至还有一个 python-yaml 包。
  • yaml 真的很棒,我用了很多配置文件
  • @Basil:大多数人都可以轻松找到“引号”键并匹配“撇号”。对于英语用户来说,这是标准的,他们都知道该怎么做。你的用户不懂英语吗?
  • @S.Lott 我不想暗示用户无法理解它。只是不必关心引用和缩进使它更容易。
  • @Basil:对于配置文件,应该从不有任何缩进,只有最简单的引用规则。您会发现 Python 的相关子集通常比所有其他语法选择简单得多
【解决方案2】:

您的用户是否精通技术并不重要;您可以指望他们搞砸编辑文本文件。 (他们会将其保存在错误的位置。他们将使用 MS Word 编辑文本文件。他们会打错字。)我建议制作一个 gui 来验证输入并以正确的格式和位置创建配置文件.在 Tkinter 中创建的简单 gui 可能会满足您的需求。

【讨论】:

  • 我同意你的看法。但目前该程序只是命令行,它会保持这种状态一段时间。
  • 您仍然可以使用raw_input 创建一个请求配置选项并正确保存它们的程序。如果您必须让您的用户直接编辑文件,我会选择 joesy 的 yaml 答案。
【解决方案3】:

这样的解决方案有什么缺点:

ch = 'serveradress = %s\nport = %s\nssl = %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 exclusively\n"\
          +"  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 exclusively\n"\
          +"  Re-choose the ssl option : "


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

.

PS

我在 jonesy 的帖子中读到可能必须引用配置文件中的值。如果是这样,并且您希望用户不必自己写引号,您只需添加

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

.

编辑

ch = 'serveradress = %s\nport = %s\nssl = %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 :\n\n" \
           + ch % tuple(li) \
           + "\n==============================================="\
           + "\n\nDo 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  -*-'

【讨论】:

  • 我认为总的来说这是个好主意。我看到的唯一问题是,如果您打错字,则必须重新输入整个内容。但我想我会在文件中添加像你的(“..you must enter digits exclusive”)这样的 cmets。
  • @Basil 嗯,可以改正。给我一点时间,我会建议你改进。
【解决方案4】:

我一直在使用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')

我还编写了一个函数,使用该 OPTIONS 列表创建示例配置文件。

    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"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-24
    • 2016-12-24
    相关资源
    最近更新 更多