zoukankan      html  css  js  c++  java
  • python配置文件INI/TOML/YAML/ENV的区别

    机翻,如有差异,请查看原文地址 https://hackersandslackers.com/simplify-your-python-projects-configuration/

    有一天,我们每个人都会死。也许我们会光荣地走出去,过上幸福的生活后盖章。当我们吸取了无法再继续的无用职业的最后一根稻草时,我们中的一些人可能会内心死亡。无论您的死亡是肉体死亡还是精神死亡,都可以肯定有一件事:您的雇主和同事会认为您永远对他们死。

    办公文化使奇怪的成语永存,我最喜欢的是永恒的“被公共汽车撞”的陈词滥调。多年来,每家公司都有相当一部分经验丰富的员工,他们积累了宝贵的知识。随着公司发现自己越来越依赖这些贡献者,组织上的谢意开始转向一种偏执狂。没有人会怀疑:“ 如果我们最好的员工被公交车撞到怎么办?

    我感谢一个组织的诗意正义,在剥削员工后无奈。也就是说,还有其他原因可确保您编写的代码易于他人阅读和使用。如果计划构建可继续运行的软件,则需要从逻辑上构建应用程序开始。让我们从第一个方框开始:项目配置。

    我们可以使用许多文件类型来存储和访问整个项目中的重要变量。诸如iniYAML或what-have 等文件类型都具有在结构化(或非结构化)层次结构中存储信息的独特方式。根据项目的性质,这些文件结构中的每一个都可以很好地为您服务或妨碍您的工作。我们将研究所有这些选项的优势,以及如何使用其相应的Python库解析这些配置。

    认识竞争者

    格式化的方法不止一种,但是在现代软件中格式化配置文件的方法甚至更多。我们将介绍一些用于处理项目配置的最常见文件格式(initomlyamlconfjsonenv)和解析它们的Python库。

    INI文件

    ini文件可能是我们可以使用的最直接的配置文件。ini文件非常适合较小的项目,主要是因为这些文件仅支持1级深的层次结构。ini文件本质上是平面文件,但变量可以属于组。下面的示例演示了具有相同主题的变量如何可以归入一个通用标题,例如_[DATABASE][LOGS]_:

    config.ini

    [APP]
    ENVIRONMENT = development
    DEBUG = False
    
    [DATABASE]
    USERNAME: root
    PASSWORD: p@ssw0rd
    HOST: 127.0.0.1
    PORT: 5432
    DB: my_database
    
    [LOGS]
    ERRORS: logs/errors.log
    INFO: data/info.log
    
    [FILES]
    STATIC_FOLDER: static
    TEMPLATES_FOLDER: templates
    

    这种结构无疑使人们更容易理解事物,但是这种结构的实用性超出了美学。让我们使用Python的configparser库解析此文件,以了解实际情况。我们首先将test.ini的内容保存到一个名为config的变量中:

    config.py

    import configparser
    
    config = configparser.ConfigParser()
    config.read('~/Desktop/config.ini')
    

    调用read()的ini文件确实比普通商店的数据更为; 实际上,我们的config变量现在是其自己的唯一数据结构,从而允许我们使用各种方法来读取和写入配置值。尝试跑步print(config)看看自己:

    <configparser.ConfigParser object at 0x10e58c390>
    

    存在配置文件只是为了提取值。configparser允许我们以多种方式执行此操作。下面的每一行都返回127.0.0.1

    config.get('DATABASE', 'HOST')
    config['DATABASE']['HOST']
    

    对于期望接收特定数据类型的值,configparser有许多类型检查方法来检索我们正在寻找的数据结构中的值。该命令config.getboolean('APP', 'DEBUG')将正确返回布尔值False,而不是一个字符串“ False”,这显然对我们的应用程序有问题。如果将我们的值DEBUG设置为布尔值以外的值,config.getboolean()则会抛出错误。configparser还有许多其他类型检查方法,例如getint()getfloat()等等。

    configparser的功能   并不止于此。我们可以详细介绍该库编写新配置值,检查键是否存在等的能力,但我们不可以。

    TOML文件

    乍看起来,TOML文件似乎与ini文件共享_某些_语法相似之处,但支持更广泛的数据类型以及值本身之间的关系。TOML文件还迫使我们提前更清楚地了解数据结构,而不是像configparser那样_在_解析_后_确定它们。

    在Python中解析TOML文件由一个适当地称为toml的库处理,在我们去那里之前,让我们看看TOML的炒作是什么。

    TOML变量类型

    TOML文件通过键/值对定义变量,方式与ini文件类似。Ť HESE对被称为_密钥_。但是,与ini文件不同,TOML希望将键的值存储为打算用作键的数据类型。打算解析为字符串的变量_必须_作为值存储在引号中,而布尔值必须存储为原始的truefalse值。这消除了我们配置的许多歧义:我们不需要诸如getboolean()TOML文件之类的方法。

    TOML文件可以支持令人印象深刻的变量类型目录。TOML支持的一些更令人印象深刻的变量类型包括DateTime本地时间数组float甚至十六进制值

    config.toml

    [project]
    name: "Faceback"
    description: "Powerful AI which renders the back of somebody's head, based on their face."
    version: "1.0.0"
    updated: 1979-05-27T07:32:00Z
    author = "Todd Birchard"
    
    ...
    

    TOML文件结构

    TOML文件中带括号的部分称为密钥可以存在于表的内部或外部,如下面的示例所示。您会注意到,这些并不是TOML文件中仅有的两个元素:

    config.toml

    # Keys
    title = "My TOML Config"
    
    
    # Tables
    [project]
    name = "Faceback"
    description = "Powerful AI which renders the back of somebody's head, based on their face."
    version = "1.0.0"
    updated = 1979-05-27T07:32:00Z
    author = "Todd Birchard"
    
    [database]
    host = "127.0.0.1"
    password = "p@ssw0rd"
    port = 5432
    name = "my_database"
    connection_max = 5000
    enabled = true
    
    
    # Nested `tables`
    [environments]
      [environments.dev]
      ip = "10.0.0.1"
      dc = "eqdc10"
      [environments.staging]
      ip = "10.0.0.2"
      dc = "eqdc10"
      [environments.production]
      ip = "10.0.0.3"
      dc = "eqdc10"
    
    # Array of Tables
    [[testers]]
    id = 1
    username = "JohnCena"
    password = "YouCantSeeMe69"
    
    [[testers]]
    id = 3
    username = "TheRock"
    password = "CantCook123"
    

    如表中所示,TOML支持“嵌套表”的概念,该[environments]表后面带有多个子表。通过使用点符号,我们能够创建表的关联,这意味着它们是同一元素的不同实例。

    同样有趣的是概念“表列”,它做什么用发生[[testers]]。双括号中的表会自动添加到数组中,其中数组中的每个项目都是具有相同名称的表。可视化此处发生情况的最佳方法是使用JSON等价物:

    {
      "testers": [
        { "id": 1, "username": "JohnCena", "password": "YouCantSeeMe69" },
        { "id": 2, "username": "TheRock", "password": "CantCook123" }
      ]
    }
    

    解析TOML

    足够使用TOML作为标准,让我们获取数据:

    import toml
    config = toml.load('/Users/toddbirchard/Desktop/config.toml')
    print(config)
    

    加载TOML文件立即返回字典:

    {'title': 'My TOML Config',
     'project': {'name': 'Faceback',
      'description': "Powerful AI which renders the back of somebody's head, based on their face.",
      'version': '1.0.0',
      'updated': datetime.datetime(1979, 5, 27, 7, 32, tzinfo=<toml.tz.TomlTz object at 0x107b82390>),
      'author': 'Todd Birchard'},
     'database': {'host': '127.0.0.1',
      'password': 'p@ssw0rd',
      'port': 5432,
      'name': 'my_database',
      'connection_max': 5000,
      'enabled': True},
     'environments': {'dev': {'ip': '10.0.0.1', 'dc': 'eqdc10'},
      'staging': {'ip': '10.0.0.2', 'dc': 'eqdc10'},
      'production': {'ip': '10.0.0.3', 'dc': 'eqdc10'}},
     'testers': [{'id': 1, 'username': 'JohnCena', 'password': 'YouCantSeeMe69'},
      {'id': 1, 'username': 'TheRock', 'password': 'CantCook123'}]}
    

    config抓取值就像使用任何字典一样容易:

    # Retrieving a dictionary
    config['project']
    config.get('project')
    
    # Retrieving a value
    config['project']['author']
    config.get('project').get('author')
    

    YAML配置

    YAML文件格式已经成为配置的人群首选,大概是因为它们易于阅读。那些熟悉YAML规范的人会告诉您,YAML _远_不是一种优雅的文件格式,但这似乎并没有阻止任何人。

    YAML文件利用空格来定义变量层次结构,这似乎引起了许多开发人员的共鸣。查看示例YAML配置可能是什么样的:

    config.yaml

    appName: appName
    logLevel: WARN
    
    AWS:
        Region: us-east-1
        Resources:
          EC2:
            Type: "AWS::EC2::Instance"
            Properties:
              ImageId: "ami-0ff8a91507f77f867"
              InstanceType: t2.micro
              KeyName: testkey
              BlockDeviceMappings:
                -
                  DeviceName: /dev/sdm
                  Ebs:
                    VolumeType: io1
                    Iops: 200
                    DeleteOnTermination: false
                    VolumeSize: 20
          Lambda:
              Type: "AWS::Lambda::Function"
              Properties:
                Handler: "index.handler"
                Role:
                  Fn::GetAtt:
                    - "LambdaExecutionRole"
                    - "Arn"
                Runtime: "python3.7"
                Timeout: 25
                TracingConfig:
                  Mode: "Active"
    
    routes:
      admin:
        url: /admin
        template: admin.html
        assets:
            templates: /templates
            static: /static
      dashboard:
        url: /dashboard
        template: dashboard.html
        assets:
            templates: /templates
            static: /static
      account:
        url: /account
        template: account.html
        assets:
            templates: /templates
            static: /static
    
    databases:
      cassandra:
        host: example.cassandra.db
        username: user
        password: password
      redshift:
        jdbcURL: jdbc:redshift://<IP>:<PORT>/file?user=username&password=pass
        tempS3Dir: s3://path/to/redshift/temp/dir/
      redis:
        host: hostname
        port: port-number
        auth: authentication
        db: databaseconfig.yaml
    

    显而易见,YAML配置_易于编写和理解_。上面的YAML文件能够完成我们在TOML文件中看到的相同类型的复杂层次结构。但是,我们不需要显式设置变量数据类型,也不需要花时间来理解诸如表****数组之类的概念。可以轻易地辩称,YAML的易用性并不能证明其缺点。不要花太多时间考虑这个问题:我们在这里谈论配置文件。

    我认为我们都可以同意的一点是,YAML肯定比JSON配置更胜一筹。这是与JSON文件相同的配置:

    config.json

    {
       "appName": "appName",
       "logLevel": "WARN",
       "AWS": {
          "Region": "us-east-1",
          "Resources": {
             "EC2": {
                "Type": "AWS::EC2::Instance",
                "Properties": {
                   "ImageId": "ami-0ff8a91507f77f867",
                   "InstanceType": "t2.micro",
                   "KeyName": "testkey",
                   "BlockDeviceMappings": [
                      {
                         "DeviceName": "/dev/sdm",
                         "Ebs": {
                            "VolumeType": "io1",
                            "Iops": 200,
                            "DeleteOnTermination": false,
                            "VolumeSize": 20
                         }
                      }
                   ]
                }
             },
             "Lambda": {
                "Type": "AWS::Lambda::Function",
                "Properties": {
                   "Handler": "index.handler",
                   "Role": {
                      "Fn::GetAtt": [
                         "LambdaExecutionRole",
                         "Arn"
                      ]
                   },
                   "Runtime": "python3.7",
                   "Timeout": 25,
                   "TracingConfig": {
                      "Mode": "Active"
                   }
                }
             }
          }
       },
       "routes": {
          "admin": {
             "url": "/admin",
             "template": "admin.html",
             "assets": {
                "templates": "/templates",
                "static": "/static"
             }
          },
          "dashboard": {
             "url": "/dashboard",
             "template": "dashboard.html",
             "assets": {
                "templates": "/templates",
                "static": "/static"
             }
          },
          "account": {
             "url": "/account",
             "template": "account.html",
             "assets": {
                "templates": "/templates",
                "static": "/static"
             }
          }
       },
       "databases": {
          "cassandra": {
             "host": "example.cassandra.db",
             "username": "user",
             "password": "password"
          },
          "redshift": {
             "jdbcURL": "jdbc:redshift://<IP>:<PORT>/file?user=username&password=pass",
             "tempS3Dir": "s3://path/to/redshift/temp/dir/"
          },
          "redis": {
             "host": "hostname",
             "port": "port-number",
             "auth": "authentication",
             "db": "database"
          }
       }
    }
    

    告诉我一个比YAML更喜欢JSON的人,我将向您展示一个受虐狂,否认他们对AWS的供应商锁定。

    在Python中解析YAML

    我建议使用Python _Confuse_库(一个软件包名称,一定会引起公司信息安全团队的注意)。

    Confuse允许我们与YAML文件进行交互,几乎与JSON进行交互,除了.get()在遍历树层次结构结束时指定的例外外,如下所示:

    config = confuse.Configuration('MyApp', __name__)
    
    config['AWS']['Lambda']['Runtime'].get()
    

    .get()可以接受数据类型值,例如_int。_这样做可以确保我们获得的值实际上是我们所期望的模式,这是一个很好的功能。

    验证者

    Confuse的文档详细介绍了从YAML文件中提取的值的其他验证方法。方法,如as_filename()as_number()as_str_seq()基本上做你希望他们到什么。

    CLI配置

    Confuse还进入了构建CLI的领域,允许我们使用YAML文件来通知可传递给CLI的参数及其潜在值:

    config = confuse.Configuration('myapp')
    parser = argparse.ArgumentParser()
    parser.add_argument('--foo', help='a parameter')
    args = parser.parse_args()
    config.set_args(args)
    print(config['foo'].get())
    

    您可以在这里做很多事情。

    .ENV文件

    环境变量是一种将敏感信息保持在项目代码库之外的好方法。我们可以用多种不同的方式存储环境变量,最简单的方法是通过命令行:

    $ export MY_VARIABLE=AAAAtpl%2Bkvro%2BoQ9wRg77VUEpQv%2F
    

    只要您当前的终端会话处于打开状态,以这种方式存储的变量将一直存在,因此在测试之外对我们没有多大帮助。如果我们要MY_VARIABLE坚持下去,可以将以上export行添加到.bash_profile(或等效文件)中,以确保MY_VARIABLE在系统范围内始终存在。

    特定于项目的变量更适合驻留在我们项目目录中的.env文件。为了上帝的爱,请勿将这些文件提交给GITHUB。

    假设我们有一个.env文件,其中包含与项目相关的变量,如下所示:

    FLASK_ENV=development
    FLASK_APP=wsgi.py
    COMPRESSOR_DEBUG=True
    STATIC_FOLDER=static
    TEMPLATES_FOLDER=templates
    

    .env

    现在,我们可以使用内置的Python提取这些值os.environ

    config.py

    """App configuration."""
    from os import environ
    
    
    class Config:
        """Set configuration vars from .env file."""
    
        # General Config
        SECRET_KEY = environ.get('SECRET_KEY')
        FLASK_APP = environ.get('FLASK_APP')
        FLASK_ENV = environ.get('FLASK_ENV')
    
        # Flask-Assets
        LESS_BIN = environ.get('LESS_BIN')
        ASSETS_DEBUG = environ.get('ASSETS_DEBUG')
        LESS_RUN_IN_DEBUG = environ.get('LESS_RUN_IN_DEBUG')
    

    随便使用您想要的

    显然,有很多方法可以在Python中设置环境和项目变量。我们可能会花费一整天的时间来剖析配置文件类型的利弊。这是我们肯定不想过分思考的生活的一个方面。

    此外,我需要反思自己的生活。我只写了两千个关于配置文件的利弊的词,在意识到自己的生活毫无意义之前,我宁愿忘记这些词。

  • 相关阅读:
    78. Subsets
    93. Restore IP Addresses
    71. Simplify Path
    82. Remove Duplicates from Sorted List II
    95. Unique Binary Search Trees II
    96. Unique Binary Search Trees
    312. Burst Balloons
    程序员社交平台
    APP Store开发指南
    iOS框架搭建(MVC,自定义TabBar)--微博搭建为例
  • 原文地址:https://www.cnblogs.com/jonnyan/p/13073272.html
Copyright © 2011-2022 走看看