PythonでGo言語の設定管理ライブラリViperと同様のライブラリを扱ってみる

## はじめに
Pythonでは，Go言語の設定管理ライブラリ[Viper](https://github.com/spf13/viper)を置き換えた同様のライブラリが存在します．
ライブラリ名は[Vyper](https://github.com/admiralobvious/vyper)です．
このライブラリの概要と使い方を学んだので，アウトプットを兼ねて記述します．

なお，本稿で取り扱うVyperは[EthereumのVyper](https://github.com/ethereum/vyper)とは全く異なるもの．それにより，PyPIでのパッケージ名は「[vyper-config](https://pypi.org/project/vyper-config/)」で登録されています．

## Vyperとは
まずVyperの簡易な説明から．
[こちら](https://qiita.com/hachi8833/items/fe3d5d6b1285c5ef1821#viper%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)はGo言語のViperについての投稿ですが，Viperについて知れます．

これらの機能をPythonでも行えるようにしたものが本稿のVypperで，設定ファイル，環境変数,コマンドライン引数，などの設定構成や形式を扱えます．
以下の設定構成や形式に対して，Vyperは読み込み，書き込みができます．
 
* デフォルト値の設定
* 設定ファイル（JSON、TOML、YAML）の読み込み
* 設定ファイルの変更監視と動的な再読み込み（オプション）
* 環境変数の読み込み
* リモートの設定ファイル（etcdやConsul）の読み込みと変更の監視
* コマンドライン引数からの読み込み
* バッファからの読み込み
* 明示的な値の設定

## Vyperを使う理由
アプリケーションを開発する上で，設定情報をどのような形式に保存するかを決定し，設定形式によって，どのような処理で設定情報を読み込むのかどうかを考えることがあります．Vyperを使用すると、このような考える手間を省くことができます.

また，実行環境ごとに設定情報を読み取る形式を変更したい場合もあるかもしれません．例えば，ある実行環境ではYAMLファイルで設定情報を読み取りたい．一方，別の実行環境ではコマンドライン引数で設定を指定したい．こういった場合にVyperが役に立つと考えています．

上記を裏付ける理由として，Vyperのインスタンスを生成すれば，生成したインスタンスの操作だけで，設定ファイルや環境変数などの値を読み取ったり，書き込んだりすることができ，Vyperの操作だけで処理が完結できるからです．

また,Vyperでは,以下の優先順位に基づいてアイテムが扱われます.
これは,例えば,設定ファイルとコマンドライン引数の両方から設定情報をVyperインスタンスに読み込んだとしましょう.ここで,設定ファイルのキー値とコマンドライン引数で指定したキー値が同一の場合,以下の優先順位に基づいて値が扱われ,この場合はコマンドライン引数のアイテムが優先されることになります.

* 設定する明示的な呼び出し
* コマンド引数
* 環境変数
* config
* キー/値ストア
* デフォルト

なお,Vyperでは大文字と小文字の区別がありません.

本稿では、コマンドライン引数、環境変数、config、デフォルトを扱う簡易な例で実装してみます．

## 環境
Windows10
Python 3.7.4



## インストール
まずインストールです．PYPiでは，上述の通り，Vyperは「vyper-config」というパッケージ名で登録されています．(Windows環境では，ビルドツールが事前にインストールされていなければ，以下のコマンドではエラーになります．)
その場合は，[こちら](https://qiita.com/white0221/items/b48f1b64dbd810e21702)を参照するとよろしいかと．

```cl
pip install vyper-config 
```

## 実装例
本稿では、コマンドライン引数、環境変数、config、デフォルトを扱う簡易な例で実装してみます．
### ディレクトリ構成
>src/
┣ sample.py
┣ settings.json


```settings.json
{
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    },
    "env": "dbhostjson",
    "app_name":"appjson"
}
```

 
```sample.py

import os
import argparse
from vyper import v
from env import const


# 1.コマンドライン引数
p = argparse.ArgumentParser(description="Application settings")
p.add_argument('--app-name', type=str, help='Application and process name')
p.add_argument('--env', type=str, choices=['dev', 'pre-prod', 'prod','arg-env'], help='Application env')
p.add_argument('--port', type=int, default=5000, help='Application port')


v.bind_args(p)


# 2.環境変数

#環境変数のキーと値を生成
os.environ["PHASE_ENV"] = "environment "
os.environ["PHASE_datastore.metric.host"] = "environment host" 
os.environ["env"] = "environment variable"

#主に、プレフィックスを指定すること場合とautomatic_env()の場合で読み取る
#v.set_env_prefix('phase')   
#v.bind_env('env')
#v.bind_env('PHASE_datastore.metric.host')

v.automatic_env()


# 3.config(今回はJSON）

#基本はたぶんこれ
#v.set_config_name('settings')  #ファイル名
#v.set_config_type('json')      #ファイル形式
#v.add_config_path('.')  #workディレクトリを基準に指定

#上記の方法でjsonファイルを探索することもできるが、今回はディレクト構成上、以下の一行でファイル探索可能
v.set_config_file("settings.json")

v.read_in_config()


# 4.デフォルト値
v.set_default('app_name', 12)
v.set_default('default', 'default')
v.set_default('port', 8000)


#アイテム取得

print(v.get('port'))
print(v.get('env'))
print(v.get('datastore.metric.host')) # #JSON形式の階層構造は「.」でつなぐ
print(v.get('default'))
print(v.get('app_name'))
```
#### 以下,標準出力( workディレクトリはsrc)

```
$ src> python sample.py
5000
environment variable
127.0.0.1
default
appjson

```

### （参考までに）設定に関するキーと値の対応図
| 設定構成       | port                         | datastore.metric.host | env                  | default | app_name   |
|--------------------|------------------------------|-----------------------|----------------------|---------|------------|
| コマンドライン引数 | 任意に指定(デフォルト値:500) | ×                     | 任意に指定           | ×       | 任意に指定 |
| 環境変数           | ×                            | ×                     | environment variable | ×       | ×          |
| 設定ファイル       | ×                            | 127.0.0.1             | dbhostjson           | ×       | app_json   |
| デフォルト         | 8000                         | ×                     | ×                    | default | 12         |