• python配置文件INI/TOML/YAML/ENV的区别


    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中设置环境和项目变量。我们可能会花费一整天的时间来剖析配置文件类型的利弊。这是我们肯定不想过分思考的生活的一个方面。

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

  • 相关阅读:
    Echrarts的基本API
    Echarts中Option属性设置
    大数据ETL处理时遇到的坑
    模块设计模式
    PAT (Advanced Level) Practice 代码
    递归中的 DFS 与 DP 比较
    CF 1557 D. Ezzat and Grid
    CF 1557 C. Moamen and XOR
    CF 1555 E. Boring Segments
    CF 1555 D. Say No to Palindromes
  • 原文地址:https://www.cnblogs.com/mouseleo/p/14305307.html
Copyright © 2020-2023  润新知