オルトプラスに入社してやったこと！！その１（PythonでTOMLを使うための仕組み）

本記事は [オルトプラス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/altplus2023) の12/05の記事です。

# はじめに

どうも！！オルトプラスの浦谷（urachooooooo）です

所属は技術部SREでテックスペシャリストとして横断的にデータベースを見ています！！
肩書きはエンジニアマネージャーですが生粋のDBAです！！

本記事ではPythonについて書いていきます。
それでは5日目のアドベントカレンダー開始しますー！！

# 概要
アジェンダとしては「PythonでTOMLを使うための仕組み」についてです。
こちらについてはサンプルスクリプトを使用して解説していきます。

# 詳細
オルトプラスに入社してからPythonを使用して色々と自動化したりツールを作成したりしているのですが、自分はPythonのコンフィグファイルにTOMLを使用することにしました！！

まずはじめにやったこととして「TOMLを読むための共通モジュールを作る」です。

この共通モジュールを使用することで以降作成するツールで使用することが可能です（楽したいですし💦）

TOMLを使用するためのシンプルなスクリプトを使用して解説していきます！！

## その前にTOMLについて
TOMLは「Tom's Obvious Minimal Language」の略で、TOMLは設定ファイルのフォーマットの1つです。

[TOML](https://toml.io/en/ "TOML")のサイトでは下記の様に記載されています。
- 人間用の設定ファイル形式（A config file format for humans.）
- トムの明瞭で最小の言語（Tom's Obvious, Minimal Language.）

簡単に説明するとTOMLは
>可読性が高く、様々なネイティブ型（Key/Value PairsやArrays）があり、様々な言語（PHPやPython）に対応しています

## ソースコードについて
サンプルの「ソースコード構成」及び、「ソースコード内容」についてこちらに記載していきます。

### ソースコード構成
:::note info
「toml-config-display」はTOMLを実行するためのプロジェクト（ROOT）です。
下記の構成通りにサンプルスクリプトを配置することで今回説明する内容を再現することができます
:::
- 
  ```shell
  ├── tcd_setup.py
  │
  └── toml-config-display
      ├── config
      │   ├── common.toml
      │   ├── tcd_general.toml
      │   └── tcd_tidb.toml
      ├── requirements.txt
      └── tomlcd
          ├── __init__.py
          ├── __main__.py
          ├── display_general_toml.py
          ├── display_tidb_toml.py
          └── libs
              ├── __init__.py
              ├── display_toolkit.py
              ├── tcd_config.py
              └── toml_toolkit.py
  ```

### ソースコード内容
:::note info
セットアップスクリプトは「tcd_setup.py」と「requirements.txt」になります
- [tcd_setup.py](#tcd_setup_py)
    - 実行すると「[ソースコード構成](#ソースコード構成)」と同じファイルを生成します
    - 生成するファイルは空ファイルになります
　　
- [requirements.txt](#requirements_txt)
    - 実行環境構築（pipインストール）時に使用します
:::
:::note info
サンプルスクリプトは「[config](#config)」・「[libs](#libs)」・「[tomlcd](#tomlcd)」で構成されています。
尚、「tomlcd」がプロジェクトになります
:::
#### setup
##### tcd_setup_py
- 
  ```python:tcd_setup.py
  #!/usr/bin/env python3
  # -*- coding: utf-8 -*-
  
  """
  GLOBAL変数初期化
  """
  # ライブラリインポート
  import os             # OS
  import sys            # SYS
  import textwrap       # ヒアドキュメント
  import inspect        # ファンクション名取得
  import pathlib        # touch
  
  # コンスタント
  TCD_BASE_PATH = os.path.abspath(".")
  TCD_PROJECT_PATH = os.path.join(TCD_BASE_PATH, "toml-config-display")
  TCD_CONFIG_PATH = os.path.join(TCD_PROJECT_PATH, "config")
  TCD_TOMLCD_PATH = os.path.join(TCD_PROJECT_PATH, "tomlcd")
  TCD_LIBS_PATH = os.path.join(TCD_TOMLCD_PATH, "libs")
  
  """
  tcd_setup.py
  --------------------------------------------------------
  toml-config-display（tcd）環境セットアップモジュール
  """
  
  def f_mkdir(in_args_dir_info):
      try:
          f_display_division("mkdir", "start")
          for dir_info in in_args_dir_info:
              print(dir_info)
              os.makedirs(dir_info, exist_ok=True)
          f_display_division("mkdir", "end")
          return True
  
      except:
          function_name = inspect.currentframe().f_code.co_name
          error_type, error_value, error_stacktrace = sys.exc_info()
          f_display_error(function_name, error_type, error_value, error_stacktrace)
          return False
  
  def f_file_touch(in_args_file_info):
      try:
          f_display_division("touch", "start")
          for file_fullpath in in_args_file_info:
              print(file_fullpath)
              file_info = pathlib.Path(file_fullpath)
              file_info.touch()
          f_display_division("touch", "end")
          return True
  
      except:
          function_name = inspect.currentframe().f_code.co_name
          error_type, error_value, error_stacktrace = sys.exc_info()
          f_display_error(function_name, error_type, error_value, error_stacktrace)
          return False
  
  def f_execute_confirmation(in_execute_message):
      # メッセージ設定
      enter_support_message = textwrap.dedent('''
      {execute_message}
      Please enter 'yes' or 'no' [y/N]: 
      ''').format(execute_message=in_execute_message).strip()
  
      try:
          while True:
              choice = input(enter_support_message).lower()
              if choice in ["y", "yes"]:
                  return True
              elif choice in ["n", "no"]:
                  return False
      except:
          function_name = inspect.currentframe().f_code.co_name
          error_type, error_value, error_stacktrace = sys.exc_info()
          f_display_error(function_name, error_type, error_value, error_stacktrace)
          return False
  
  def f_display_division(in_action_type, in_division_type):
      if in_division_type == "start":
          start_message = textwrap.dedent('''
          [{action_type}]
          +---------------------------------------------------------------------------------+
          ''').format(action_type=in_action_type).strip()
          print(start_message)
      
      elif in_division_type == "end":
          end_message = textwrap.dedent('''
          +---------------------------------------------------------------------------------+
          ''').strip()
          print("{} {}".format(end_message, "\n"))
  
  def f_display_error(in_function_name, in_error_type, in_error_value, in_error_stacktrace):
      # エラー情報設定
      error_message = textwrap.dedent('''
      [{function_name}]
      +---------------------------------------------------------------------------------+
          error_type  :
            {error_type}
      
          error_value :
            {error_value}
  
          error_stacktrace :
            {error_stacktrace}
      +---------------------------------------------------------------------------------+
      ''').format(function_name=in_function_name, error_type=in_error_type, error_value=in_error_value, error_stacktrace=in_error_stacktrace).strip()
  
      # エラー情報表示
      print("{} {}".format(error_message, "\n"))
  
  def f_display_finish_message(in_message):
      # メッセージ設定
      finish_message = textwrap.dedent('''
      ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆
          {message} have been completed !!!
      ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆
      ''').format(message=in_message)
      print("{}".format(finish_message, "\n"))
  
  def f_create_template():
      ########################
      # ディレクトリ作成
      ########################
      args_dir_info = []
      args_dir_info.extend([TCD_CONFIG_PATH, TCD_LIBS_PATH])
      """ f_mkdir """
      result = f_mkdir(args_dir_info)
      if result == True:
          ########################
          # 空ファイル作成
          ########################
          args_file_info = []
          args_file_info.extend([os.path.join(TCD_CONFIG_PATH, "common.toml"), 
                                 os.path.join(TCD_CONFIG_PATH, "tcd_general.toml"),
                                 os.path.join(TCD_CONFIG_PATH, "tcd_tidb.toml"),
                                 os.path.join(TCD_TOMLCD_PATH, "__init__.py"),
                                 os.path.join(TCD_TOMLCD_PATH, "__main__.py"),
                                 os.path.join(TCD_TOMLCD_PATH, "display_general_toml.py"),
                                 os.path.join(TCD_TOMLCD_PATH, "display_tidb_toml.py"),
                                 os.path.join(TCD_LIBS_PATH, "__init__.py"),
                                 os.path.join(TCD_LIBS_PATH, "display_toolkit.py"),
                                 os.path.join(TCD_LIBS_PATH, "tcd_config.py"),
                                 os.path.join(TCD_LIBS_PATH, "toml_toolkit.py"),
                                 os.path.join(TCD_PROJECT_PATH, "requirements.txt")])
  
          """ f_file_touch """
          result = f_file_touch(args_file_info)
          if result == True:
              """ f_display_finish_message """
              f_display_finish_message("{}".format("tcd_setup"))
  
  def f_main():
      tcd_setup_message = textwrap.dedent('''
      +---------------------------------------------------------------------------------+
        スクリプト実行パス「{tcd_base_path}」配下に
        TOML動作確認用の環境を構築します
        よろしいでしょうか？                                           
        
        ※パーミッションが有効でない場合はエラーになります
      +---------------------------------------------------------------------------------+
      ''').format(tcd_base_path=TCD_BASE_PATH).strip()
      result = f_execute_confirmation(tcd_setup_message)
      if result == True:
          """ f_create_template """
          f_create_template()
  
  """
  main
  """
  if __name__ == '__main__':
      f_main()
  ```

##### requirements_txt
-
  ```txt:requirements.txt
  toml==0.10.2
  ```

#### config
:::note info
TOMLフォーマットの設定ファイルです
- [common.toml](#common_toml)
    - 便宜上分けています（今回のプロジェクト以外でも使用可能なパラメータのため）
    - 表示では使用しません（判定で使用）
　　
- [tcd_general.toml](#tcd_general_toml)
    - Key-Valueの最もシンプルな構成
    - このTOMLの内容を表示します
　　
- [tcd_tidb.toml](#tcd_tidb_toml)
    - TiDBの「[Server recommendations](https://docs.pingcap.com/tidb/stable/hardware-and-software-requirements#server-recommendations "Software and Hardware Recommendations")」をベースとした階層と配列を含んだ構成
    - このTOMLの内容を表示します
:::

:::note info
TOMLの記述について
- テーブル（TABLE）
    - パラメータのカテゴリーとして分けている「[]」で囲われている値は「テーブル」と呼ばれています
        - 例えば「common.toml」の「[environment_info]」はテーブルであり、「tcd_general.toml」の「[setup_info]」もテーブルです
　　
- キー（KEY）
    - 値を検索するためのキー
        - 例えば「common.toml」の「ENVIRONMENT_LIST」はキーであり、「tcd_general.toml」の「TCD_ROOT_DIR」や「TCD_CONFIG_DIR」もキーです
　　
- 値（VALUE）
    - キーに対する値
        - 例えば、「common.toml」の「['development', 'production']」は値で、「tcd_general.toml」の「"toml-config-display"」や「"config"」も値です
:::

##### common_toml
- 
  ```toml:common.toml
  [environment_info]
  ENVIRONMENT_LIST  = ['development', 'production']
  ```

##### tcd_general_toml
-
  ```toml:tcd_general.toml
  [setup_info]
  TCD_ROOT_DIR = "toml-config-display"
  TCD_CONFIG_DIR = "config"
  TCD_SCRIPT_DIR = "tomlcd"
  TCD_LIBS_DIR = "libs"
  
  [config_info]
  TCD_GENERAL_TOML = "tcd_general.toml"
  TCD_TIDB_TOML = "tcd_tidb.toml"
  
  [libs_info]
  TCD_CONFIG_PY = "tcd_config.py"
  TOML_TOOLKIT_PY = "toml_toolkit.py"
  DISPLAY_TOOLKIT_PY = "display_toolkit.py"
  
  [script_info]
  DISPLAY_GENERAL_TOML_PY = "display_general_toml.py"
  DISPLAY_TIDB_TOML_PY = "display_tidb_toml.py"
  ```

##### tcd_tidb_toml
-
  ```toml:tcd_tidb.toml
  [development]
      [development.tidb_cluster]
      INSTANCE_TYPE = "t4g.2xlarge"
      RESOURCE.CPU = "8 core+"
      RESOURCE.MEMORY = "16 GB+"
      RESOURCE.STORAGE = "No special requirements"
      RESOURCE.NETWORK = "Gigabit network card"
      RESOURCE.MINIMUM_REQUIREMENT = 1
      SERVER_LIST = [
          "tidb-server-01"
      ]
  
      [development.pd_cluster]
      INSTANCE_TYPE = "t4g.xlarge"
      RESOURCE.CPU = "4 core+"
      RESOURCE.MEMORY = "8 GB+"
      RESOURCE.STORAGE = "SAS, 200 GB+"
      RESOURCE.NETWORK = "Gigabit network card"
      RESOURCE.MINIMUM_REQUIREMENT = 1
      SERVER_LIST = [
          "pd-server-01"
      ]
  
      [development.tikv_cluster]
      INSTANCE_TYPE = "i4g.2xlarge"
      RESOURCE.CPU = "8 core+"
      RESOURCE.MEMORY = "32 GB+"
      RESOURCE.STORAGE = "SAS, 200 GB+"
      RESOURCE.NETWORK = "Gigabit network card"
      RESOURCE.MINIMUM_REQUIREMENT = 3
      SERVER_LIST = [
          "tikv-server-01", 
          "tikv-server-02",
          "tikv-server-03"
      ]
      
  [production]
      [production.tidb_cluster]
      INSTANCE_TYPE = "Im4gn.4xlarge"
      RESOURCE.CPU = "16 core+"
      RESOURCE.MEMORY = "48 GB+"
      RESOURCE.STORAGE = "SSD"
      RESOURCE.NETWORK = "10 Gigabit network card (2 preferred)"
      RESOURCE.MINIMUM_REQUIREMENT = 2
      SERVER_LIST = [
          "tidb-server-01", 
          "tidb-server-02"
      ]

      [production.pd_cluster]
      INSTANCE_TYPE = "Im4gn.2xlarge"
      RESOURCE.CPU = "8 core+"
      RESOURCE.MEMORY = "16 GB+"
      RESOURCE.STORAGE = "SSD"
      RESOURCE.NETWORK = "10 Gigabit network card (2 preferred)"
      RESOURCE.MINIMUM_REQUIREMENT = 3
      SERVER_LIST = [
          "pd-server-01", 
          "pd-server-02",
          "pd-server-03"
      ]
  
      [production.tikv_cluster]
      INSTANCE_TYPE = "i4g.4xlarge"
      RESOURCE.CPU = "16 core+"
      RESOURCE.MEMORY = "64 GB+"
      RESOURCE.STORAGE = "SSD"
      RESOURCE.NETWORK = "10 Gigabit network card (2 preferred)"
      RESOURCE.MINIMUM_REQUIREMENT = 3
      SERVER_LIST = [
          "tikv-server-01", 
          "tikv-server-02",
          "tikv-server-03"
      ]
  
      [production.tiflash_cluster]
      INSTANCE_TYPE = "i4g.16xlarge"
      RESOURCE.CPU = "48 core+"
      RESOURCE.MEMORY = "128 GB+"
      RESOURCE.STORAGE = "1 or more SSDs"
      RESOURCE.NETWORK = "10 Gigabit network card (2 preferred)"
      RESOURCE.MINIMUM_REQUIREMENT = 2
      SERVER_LIST = [
          "tiflash-server-01", 
          "tiflash-server-02"
      ]
  
      [production.monitor]
      INSTANCE_TYPE = "t4g.2xlarge"
      RESOURCE.CPU = "8 core+"
      RESOURCE.MEMORY = "16 GB+"
      RESOURCE.STORAGE = "SAS"
      RESOURCE.NETWORK = "Gigabit network card"
      RESOURCE.MINIMUM_REQUIREMENT = 1
      SERVER_LIST = [
          "monitor-server-01"
      ]
  ```

#### libs
:::note info
toml-config-displayで使用するPythonのモジュールです
- [toml_toolkit.py](#toml_toolkit_py)
    - TOMLを読み込むための共通モジュール
　　
- [tcd_config.py](#tcd_config_py)
    - 「toml_toolkit.py」を使用してTOMLファイルの読み込み及び、設定ファイル用に加工するためのモジュール
　　
- [display_toolkit.py](#display_toolkit_py)
    - 表示用の共通モジュール
:::

##### toml_toolkit_py
-
  ```python:toml_toolkit.py
  #!/usr/bin/env python3
  # -*- coding: utf-8 -*-
  
  """
  GLOBAL変数初期化
  """
  # ライブラリインポート
  import toml           # TOML
  
  """
  toml_toolkit.py
  --------------------------------------------------------
  TOML共通モジュール
  """
  def f_read_toml(in_toml_path, in_key):
      with open(in_toml_path, "r") as file:
          ret_value = toml.load(file)
          if in_key != None:
              ret_value = ret_value[in_key]
      return ret_value
  
  def f_get_toml_table(in_toml_path, in_key):
      toml_object = f_read_toml(in_toml_path, in_key)
      ret_value = []
      for key in toml_object.keys():
          if isinstance(toml_object[key], dict):
              ret_value.append(key)
      return ret_value
  
  def f_search_toml(in_toml_object, in_toml_table, in_toml_key):
      ret_value = in_toml_object[in_toml_table][in_toml_key]
      return ret_value
  ```

##### tcd_config_py
-
  ```python:tcd_config.py
  #!/usr/bin/env python3
  # -*- coding: utf-8 -*-
  
  """
  GLOBAL変数初期化
  """
  # ライブラリインポート
  import os                # OS
  import libs.toml_toolkit as toml_toolkit
  
  # コンスタント
  TCD_BASE_PATH = os.path.abspath("..")
  TCD_CONFIG_PATH = os.path.join(TCD_BASE_PATH, "config")
  COMMON_TOML_PATH = os.path.join(TCD_CONFIG_PATH, "common.toml")
  TCD_GENERAL_TOML_PATH = os.path.join(TCD_CONFIG_PATH, "tcd_general.toml")
  TCD_TIDB_TOML_PATH = os.path.join(TCD_CONFIG_PATH, "tcd_tidb.toml")
  
  """
  tcd_config.py
  --------------------------------------------------------
  tomlパラメータ設定モジュール
  """
  
  class CommonToml:
      def __init__(self):
          self.environment_list = None
  
      def read_toml(self):
          common_toml = toml_toolkit.f_read_toml(COMMON_TOML_PATH, None)
          # [environment_info]
          self.environment_list = toml_toolkit.f_search_toml(common_toml, "environment_info", "ENVIRONMENT_LIST")
  
  class GeneralToml:
      def __init__(self):
          self.tcd_root_dir = None
          self.tcd_config_dir = None
          self.tcd_script_dir = None
          self.tcd_libs_dir = None
          self.tcd_general_toml = None
          self.tcd_tidb_toml = None
          self.display_general_toml_py = None
          self.display_tidb_toml_py = None
          self.tcd_config_py = None
          self.toml_toolkit_py = None
          self.display_toolkit_py = None
  
      def read_toml(self):
          self.tcd_base_path = TCD_BASE_PATH
          tcd_general_toml = toml_toolkit.f_read_toml(TCD_GENERAL_TOML_PATH, None)
  
          # [setup_info]
          self.tcd_root_dir = toml_toolkit.f_search_toml(tcd_general_toml, "setup_info", "TCD_ROOT_DIR")
          self.tcd_config_dir = toml_toolkit.f_search_toml(tcd_general_toml, "setup_info", "TCD_CONFIG_DIR")
          self.tcd_script_dir = toml_toolkit.f_search_toml(tcd_general_toml, "setup_info", "TCD_SCRIPT_DIR")
          self.tcd_libs_dir = toml_toolkit.f_search_toml(tcd_general_toml, "setup_info", "TCD_LIBS_DIR")
  
          # [config_info]
          self.tcd_general_toml = toml_toolkit.f_search_toml(tcd_general_toml, "config_info", "TCD_GENERAL_TOML")
          self.tcd_tidb_toml = toml_toolkit.f_search_toml(tcd_general_toml, "config_info", "TCD_TIDB_TOML")
  
          # [script_info]
          self.display_general_toml_py = toml_toolkit.f_search_toml(tcd_general_toml, "script_info", "DISPLAY_GENERAL_TOML_PY")
          self.display_tidb_toml_py = toml_toolkit.f_search_toml(tcd_general_toml, "script_info", "DISPLAY_TIDB_TOML_PY")
  
          # [libs_info]
          self.tcd_config_py = toml_toolkit.f_search_toml(tcd_general_toml, "libs_info", "TCD_CONFIG_PY")
          self.toml_toolkit_py = toml_toolkit.f_search_toml(tcd_general_toml, "libs_info", "TOML_TOOLKIT_PY")
          self.display_toolkit_py = toml_toolkit.f_search_toml(tcd_general_toml, "libs_info", "DISPLAY_TOOLKIT_PY")
  
          ##############################
          # カスタム設定
          ##############################
          self.args_path_list = []
          # configパス設定
          self.config_path = os.path.join(self.tcd_base_path, self.tcd_config_dir)
          self.tcd_general_toml_fullpath = os.path.join(self.config_path, self.tcd_general_toml)
          self.tcd_tidb_toml_fullpath = os.path.join(self.config_path, self.tcd_tidb_toml)
          self.args_path_list.append([self.tcd_config_dir, self.tcd_general_toml_fullpath, self.tcd_tidb_toml_fullpath])
      
          # scriptパス設定
          self.script_path = os.path.join(self.tcd_base_path, self.tcd_script_dir)
          self.display_general_toml_py_fullpath = os.path.join(self.script_path, self.display_general_toml_py)
          self.display_tidb_toml_py_fullpath = os.path.join(self.script_path, self.display_tidb_toml_py)
          self.args_path_list.append([self.tcd_script_dir, self.display_general_toml_py_fullpath, self.display_tidb_toml_py_fullpath])
  
          # libパス設定
          libs_path = os.path.join(self.script_path, self.tcd_libs_dir)
          self.tcd_config_py_fullpath = os.path.join(libs_path, self.tcd_config_py)
          self.toml_toolkit_py_fullpath = os.path.join(libs_path, self.toml_toolkit_py)
          self.display_toolkit_py_fullpath = os.path.join(libs_path, self.display_toolkit_py)
          self.args_path_list.append([self.tcd_libs_dir, self.tcd_config_py_fullpath, self.toml_toolkit_py_fullpath, self.display_toolkit_py_fullpath])
  
  class TidbToml:
      def __init__(self, in_environment_type, in_cluster_type):
          self.environment_type = in_environment_type
          self.cluster_type = in_cluster_type
          self.cpu = None
          self.memory = None
          self.storage = None
          self.network = None
          self.minimum_requirement = None
  
      def read_toml(self):
          tcd_tidb_toml = toml_toolkit.f_read_toml(TCD_TIDB_TOML_PATH, self.environment_type)
          self.instance_type = toml_toolkit.f_search_toml(tcd_tidb_toml, self.cluster_type, "INSTANCE_TYPE")
          result = toml_toolkit.f_search_toml(tcd_tidb_toml, self.cluster_type, "RESOURCE")
          self.cpu = result["CPU"]
          self.memory = result["MEMORY"]
          self.storage = result["STORAGE"]
          self.network = result["NETWORK"]
          self.minimum_requirement = result["MINIMUM_REQUIREMENT"]
  
      def get_toml_table(self):
          result = toml_toolkit.f_get_toml_table(TCD_TIDB_TOML_PATH, self.environment_type)
          return result
  ```

##### display_toolkit_py
-
  ```python:display_toolkit.py
  #!/usr/bin/env python3
  # -*- coding: utf-8 -*-
  
  """
  GLOBAL変数初期化
  """
  # ライブラリインポート
  import textwrap       # ヒアドキュメント
  
  """
  display_toolkit.py
  --------------------------------------------------------
  表示用共通モジュール
  """
  def f_display_argument_error(in_item_name, in_argument, in_argument_list):
      environment_errorr_message = textwrap.dedent('''
      [{item_name}エラー]
      +---------------------------------------------------------------------------------+
        {item_name}に「{argument}」が設定されています。
        
        パラメータ「{item_name}」は
        {argument_list}
        を設定してください
      +---------------------------------------------------------------------------------+
      ''').format(item_name=in_item_name, argument=in_argument, argument_list=in_argument_list).strip()
      print("{} {}".format(environment_errorr_message, "\n"))
  
  def f_display_division(in_action_type, in_division_type, in_finish):
      if in_division_type == "start":
          start_message = textwrap.dedent('''
          [{action_type}]
          +---------------------------------------------------------------------------------+
          ''').format(action_type=in_action_type).strip()
          print(start_message)
      
      elif in_division_type == "finish":
          finish_message = textwrap.dedent('''
          {finish}
          +---------------------------------------------------------------------------------+
          ''').format(finish=in_finish).strip()
          print("{} {}".format(finish_message, "\n"))
  ```

#### tomlcd
:::note info
TOMLを表示するためのPythonのメインモジュールです
- [display_general_toml.py](#display_general_toml_py)
    - 「tcd_general.toml」を表示するサンプルスクリプト
　　
- [display_tidb_toml.py](#display_tidb_toml_py)
    - 「tcd_tidb.toml」を表示するサンプルスクリプト
:::

##### display_general_toml_py
-
  ```python:display_general_toml.py
  #!/usr/bin/env python3
  # -*- coding: utf-8 -*-
  
  """
  GLOBAL変数初期化
  """
  # ライブラリインポート
  import inspect           # ファンクション名取得
  import libs.tcd_config as tcd_config
  import libs.display_toolkit as display_toolkit
  
  """
  display_general_toml.py
  --------------------------------------------------------
  「tdc_general.toml」表示モジュール
  """
  
  # config read
  tcd_general_toml = tcd_config.GeneralToml()
  tcd_general_toml.read_toml()
  
  def f_display_general_toml_01():
      function_name = inspect.currentframe().f_code.co_name
      """ f_display_division """
      display_toolkit.f_display_division(function_name, "start", "")
  
      print("[{}]".format(tcd_general_toml.tcd_config_dir))
      print(tcd_general_toml.tcd_general_toml_fullpath)
      print(tcd_general_toml.tcd_tidb_toml_fullpath)
  
      print("[{}]".format(tcd_general_toml.tcd_script_dir))
      print(tcd_general_toml.display_general_toml_py_fullpath)
      print(tcd_general_toml.display_tidb_toml_py_fullpath)
  
      print("[{}]".format(tcd_general_toml.tcd_libs_dir))
      print(tcd_general_toml.tcd_config_py_fullpath)
      print(tcd_general_toml.toml_toolkit_py_fullpath)
      print(tcd_general_toml.display_toolkit_py_fullpath)
  
      """ f_display_division """
      display_toolkit.f_display_division(function_name, "finish", "")
  
  def f_display_general_toml_02():
      function_name = inspect.currentframe().f_code.co_name
      """ f_display_division """
      display_toolkit.f_display_division(function_name, "start", "")
  
      print("{}".format(inspect.currentframe().f_code.co_name))
      for args_path_list in tcd_general_toml.args_path_list:
          print("[{}]".format(args_path_list[0]))
          for path_info in args_path_list[1:]:
              print(path_info)
  
      """ f_display_division """
      display_toolkit.f_display_division(function_name, "finish", "")
  
  def f_main():
      """ f_display_general_toml_01 """
      f_display_general_toml_01()
  
      """ f_display_general_toml_02 """
      f_display_general_toml_02()
  
  """
  main
  """
  if __name__ == '__main__':
      f_main()
  ```

##### display_tidb_toml_py
-
  ```python:display_tidb_toml.py
  #!/usr/bin/env python3
  # -*- coding: utf-8 -*-
  
  """
  GLOBAL変数初期化
  """
  # ライブラリインポート
  import sys            # SYS
  import textwrap       # ヒアドキュメント
  import libs.tcd_config as tcd_config
  import libs.display_toolkit as display_toolkit
  
  """
  display_tidb_toml.py
  --------------------------------------------------------
  「tdc_tidb.toml」表示モジュール
  """
  
  # config read
  common_toml = tcd_config.CommonToml()
  common_toml.read_toml()
  tcd_general_toml = tcd_config.GeneralToml()
  tcd_general_toml.read_toml()
  
  def f_check_argument(in_environment_list):
      # パラメータ設定
      in_args = sys.argv
      try:
          if len(in_args) == 3:
              return True, str(in_args[1]), str(in_args[2])
          else:
              raise argument_error
      except Exception as argument_error:
          error_message = textwrap.dedent('''
          パラメータの設定は下記の通り設定してください
          +---------------------------------------------------------------------------------+
            第1パラメータに「環境のタイプ」
            第2パラメータに「クラスタータイプ」を設定してください
          
            環境のタイプ    : {nvironment_list}
            クラスタータイプ : 
              「ALL」を入力すると「環境のタイプ」に関連する全てのクラスターが表示されます
          +---------------------------------------------------------------------------------+
          ''').format(nvironment_list=in_environment_list).strip()
          print("{} {}".format(error_message, "\n"))
          return False, None
  
  def f_display_cluster_info(in_environment_type, in_cluster_type):
      tcd_tidb_toml = tcd_config.TidbToml(in_environment_type, in_cluster_type)
      tcd_tidb_toml.read_toml()
  
      environment_message = textwrap.dedent('''
      [{cluster_type}]
      +---------------------------------------------------------------------------------+
        Instance Type: {instance_type}
        CPU     : {cpu}
        Memory  : {memory}
        Storage : {storage}
        Network : {network}                       
        Number of Instances(Minimum Requirement) : {minimum_requirement}
      +---------------------------------------------------------------------------------+
      ''').format(cluster_type=in_cluster_type, instance_type=tcd_tidb_toml.instance_type, 
                  cpu=tcd_tidb_toml.cpu, memory=tcd_tidb_toml.memory, 
                  storage=tcd_tidb_toml.storage, network=tcd_tidb_toml.network, 
                  minimum_requirement=tcd_tidb_toml.minimum_requirement).strip()
      print("{} {}".format(environment_message, "\n"))
  
  def f_main():
      """ f_check_param """
      result = f_check_argument(common_toml.environment_list)
      if result[0] == True:
          environment_type, cluster_type = result[1], result[2]
      else:
          sys.exit()
  
      if environment_type in common_toml.environment_list:
          tcd_tidb_toml = tcd_config.TidbToml(environment_type, None)
          component_list = tcd_tidb_toml.get_toml_table()
  
          if cluster_type in component_list or cluster_type.upper() == "ALL":
       
              if cluster_type.upper() == "ALL":
                  for cluster_name in component_list:
                      """ f_display_cluster_info """
                      f_display_cluster_info(environment_type, cluster_name)
              else:
                  """ f_display_cluster_info """
                  f_display_cluster_info(environment_type, cluster_type)
                  
          else:
              """ f_display_argument_error """
              display_toolkit.f_display_argument_error("cluster_type", cluster_type, component_list)
  
      else:
          """ f_display_argument_error """
          display_toolkit.f_display_argument_error("environment_type", environment_type, common_toml.environment_list)
  
  """
  main
  """
  if __name__ == '__main__':
      f_main()
  ```

## セットアップ手順
上記サンプルスクリプトをセットアップして実行するまでの手順を記載していきます

### 環境について
:::note info
スクリプトを確認した環境は下記になります。
尚、今回使用した環境はMacになりますが、環境に依存しない作りにしているため異なる環境（例えばWindows）でもスクリプトは動くかと思います
:::
- MacBook Pro（Apple M2）
    - macOS Ventura（13.5.2）
- Pythonのバージョン（3.11.4）

### 仮想環境準備
:::note warn
必要に応じて下記をインストールしてください
今回の記事で「pyenv」や「virtualenv」のインストール及び、設定に関して記載するかどうか？迷ったのですが、今回はそこまでは記載しないことにします。
:::
- pyenv
    - Pythonのバージョンを固定する場合
- virtualenv
    - 環境汚染を防止する場合
    - Pythonモジュールのバージョンを固定する場合

### 事前準備
:::note warn
ソースコード公開用のGitHubを用意してcloneする方が導入が楽なのですが、お手数ですが各スクリプトをコピーして試していただければと思います。
:::
:::note info
スクリプトの配置やPythonモジュール（TOML）をセットアップします
- スクリプトの配置について
    - セットアップスクリプトも準備していますので、手順に沿って実施ください。
　　
- Pythonモジュールのインストールについて
    - requirements.txtには今回使用したバージョンが記載されていますが、必要に応じて最新のバージョンをインストールことも可能です。
      ```shell
      python3 -m pip install toml
      ```
:::
1. スクリプトを配置
    下記のステップを実施してください
    a.　環境構築するディレクトリに移動して「[tcd_setup.py](#tcd_setup_py)」を作成
    b.　「tcd_setup.py」モジュールを実行
      ```shell
      python3 -m tcd_setup
      ```
      c.　「[ソースコード構成](#ソースコード構成)」と同じ構成であることを確認
      d.　「[ソースコード内容](#ソースコード内容)」のソースコードを作成ファイルに登録
　
  2. Pythonモジュールをインストール
    a.　Pythonモジュールのインストール状況を確認
        ```shell
        pip3 freeze | grep toml
        ```
        b.「toml-config-display」ディレクトリに移動
        c. Pythonモジュールをインストール
        ```shell
        pip3 install -r requirements.txt
        ```

### スクリプト実行
:::note info
各スクリプトを実行後にTOMLの情報を取得して表示します
- [display_general_toml.py](#display_general_toml_py)
    - 「[tcd_general.toml](#tcd_general_toml)」のデータを表示します
    - テーブルによる階層がないため値の取得方法を変化させました
      出力結果は同じですが実行しているファンクションを分けています
        - f_display_general_toml_01
            - TOMLの値を個別で表示
        - f_display_general_toml_02
            - TOMLの値を配列に格納してLOOPで表示
　　
- [display_tidb_toml.py](#display_tidb_toml_py)
    - 「[tcd_tidb.toml](#tcd_tidb_toml)」のデータを表示します
    - テーブル毎に階層を分けているため入力パラメータによって様々な条件でTOMLの内容を表示しています
        - 例）development、production、tidb_cluster、etc.
    - スクリプトの入力パラメータ
        - 第1パラメータ：development、production
        - 第2パラメータ：tidb_cluster、ALL、etc.
    - スクリプトの入力パラメータがあるため入力チェックを行います
    - スクリプトの入力チェックにはTOMLのテーブルのリストを評価しています。そのため設定パラメータによって評価の対象も動的に変化する仕組みになっています
        - 例）development、productionの階層に「staging、test、etc.」を追加するとチェック対象が自動で増えます
:::
1. スクリプトのプロジェクトパス（「[tomlcd](#tomlcd)」）へ移動
2. スクリプト実行
- 「[display_general_toml.py](#display_general_toml_py)」を実行
    - コマンド
        ```shell
        python3 -m display_general_toml
        ```
    - 結果
        ```text
        [f_display_general_toml_01]
        +---------------------------------------------------------------------------------+
        [config]
        /opt/alt/toml-config-display/config/tcd_general.toml
        /opt/alt/toml-config-display/config/tcd_tidb.toml
        [tomlcd]
        /opt/alt/toml-config-display/tomlcd/display_general_toml.py
        /opt/alt/toml-config-display/tomlcd/display_tidb_toml.py
        [libs]
        /opt/alt/toml-config-display/tomlcd/libs/tcd_config.py
        /opt/alt/toml-config-display/tomlcd/libs/toml_toolkit.py
        /opt/alt/toml-config-display/tomlcd/libs/display_toolkit.py
        +---------------------------------------------------------------------------------+ 
        
        [f_display_general_toml_02]
        +---------------------------------------------------------------------------------+
        f_display_general_toml_02
        [config]
        /opt/alt/toml-config-display/config/tcd_general.toml
        /opt/alt/toml-config-display/config/tcd_tidb.toml
        [tomlcd]
        /opt/alt/toml-config-display/tomlcd/display_general_toml.py
        /opt/alt/toml-config-display/tomlcd/display_tidb_toml.py
        [libs]
        /opt/alt/toml-config-display/tomlcd/libs/tcd_config.py
        /opt/alt/toml-config-display/tomlcd/libs/toml_toolkit.py
        /opt/alt/toml-config-display/tomlcd/libs/display_toolkit.py
        +---------------------------------------------------------------------------------+
        ```
　　
- 「[display_tidb_toml.py](#display_tidb_toml_py)」を実行
    こちらはいくつかのパターンがありますので例を表示します
    - 例）「tcd_tidb.toml」のテーブル「development」を全て表示する場合
        - コマンド
            ```shell
            python3 -m display_tidb_toml development ALL
            ```
        - 結果
            ```text
            [tidb_cluster]
            +---------------------------------------------------------------------------------+
              Instance Type: t4g.2xlarge
              CPU     : 8 core+
              Memory  : 16 GB+
              Storage : No special requirements
              Network : Gigabit network card                       
              Number of Instances(Minimum Requirement) : 1
            +---------------------------------------------------------------------------------+ 
            
            [pd_cluster]
            +---------------------------------------------------------------------------------+
              Instance Type: t4g.xlarge
              CPU     : 4 core+
              Memory  : 8 GB+
              Storage : SAS, 200 GB+
              Network : Gigabit network card                       
              Number of Instances(Minimum Requirement) : 1
            +---------------------------------------------------------------------------------+ 
            
            [tikv_cluster]
            +---------------------------------------------------------------------------------+
              Instance Type: i4g.2xlarge
              CPU     : 8 core+
              Memory  : 32 GB+
              Storage : SAS, 200 GB+
              Network : Gigabit network card                       
              Number of Instances(Minimum Requirement) : 3
            +---------------------------------------------------------------------------------+
            ```
    - 例）「tcd_tidb.toml」のテーブル「development.tidb_cluster」を表示する場合
        - コマンド
            ```shell
            python3 -m display_tidb_toml development tidb_cluster
            ```
        - 結果
            ```text
            [tidb_cluster]
            +---------------------------------------------------------------------------------+
              Instance Type: t4g.2xlarge
              CPU     : 8 core+
              Memory  : 16 GB+
              Storage : No special requirements
              Network : Gigabit network card                       
              Number of Instances(Minimum Requirement) : 1
            +---------------------------------------------------------------------------------+ 
            ```
    - 例）「tcd_tidb.toml」のテーブル「production.tikv_cluster」を表示する場合
        - コマンド
            ```shell
            python3 -m display_tidb_toml production tikv_cluster
            ```
        - 結果
            ```shell
            [tikv_cluster]
            +---------------------------------------------------------------------------------+
              Instance Type: i4g.4xlarge
              CPU     : 16 core+
              Memory  : 64 GB+
              Storage : SSD
              Network : 10 Gigabit network card (2 preferred)                       
              Number of Instances(Minimum Requirement) : 3
            +---------------------------------------------------------------------------------+
            ```

## 仕組みについての解説
:::note info
「[tcd_general.toml](#tcd_general_toml)」のデータを
[display_general_toml.py](#display_general_toml_py)でどのように取得しているのか？を最後に解説しておきます

尚、取得対象はテーブル「setup_info」のキー「TCD_CONFIG_DIR」で値「"config"」を取得する例になります
:::

### データの流れ
![display_general_toml実行の流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46914/2346757d-27ee-2e52-a952-715c6fa192ef.png)
- 上記イメージは「[display_general_toml.py](#display_general_toml_py)」を実行してから結果が表示されるまでの流れになります
    - スクリプトを実行
    - 番号の順番通りに遷移
    - 結果を表示
- 色によって識別できるようにしています
    - 同色であれば参照先や関係は同じになります

### 解説
- 上記スクリプト実行の流れを解説します
    1. 「[display_general_toml.py](#display_general_toml_py)」を実行
        - 「[tcd_config.py](#tcd_config_py)」のClass「GeneralToml」をインスタンス化
    2. インスタンス化した「GeneralToml」の「read_toml」を実行
        - 「[toml_toolkit.py](#toml_toolkit_py)」の「f_read_toml」を使用して
        「[tcd_general.toml](#tcd_general_toml)」のデータを全件ロード
        - TABLE（setup_info）、KEY（TCD_CONFIG_DIR）
        からVALUE（"config"）を取得
    3. 「tcd_general_toml.tcd_root_dir」をprintすると
    値「"config"」が表示されます

# まとめ
PythonでTOMLを使うための仕組みについて
どの様にTOMLのデータを取得しているのか？
サンプルスクリプトを使用して解説を行いました！！

今回公開しましたスクリプトは自由にお使いくださいませーー

スクリプトをそのまま使うでもよし！
自分達でもっと使いやすく修正して使うでもよし！！
今回載せたスクリプトが皆さんのお役に立てたら幸いです！！

また、Pythonで使用しないにせよ、
TOMLはシンプルで使いやすいのでお勧めですーー！！
様々な言語に対応していますので、
是非是非お使いくださいーー
（TOMLの回し者ではないですよw）