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中設置環境和項目變量。我們可能會花費一整天的時間來剖析配置文件類型的利弊。這是我們肯定不想過分思考的生活的一個方面。

此外,我需要反思自己的生活。我只寫了兩千個關於配置文件的利弊的詞,在意識到自己的生活毫無意義之前,我寧願忘記這些詞。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM