6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ZOZOAdvent Calendar 2024

Day 20

YAMLのThe Norway Problemを検知する

Last updated at Posted at 2024-12-20

The Norway Problemについて

YAMLでは、特定の文字列が意図せず特定の型に変換されてしまうことがあります。例えば、NOという文字列は真偽値のFalseとして解釈されます。どうやらこのような現象のことをThe Norway Problemというらしいです。ノルウェーをISO国コードで訳すと、NOなのでそのような名前になっているそう。

norway.yaml
country: NO
load_yaml.py
import yaml

with open('norway.yaml') as file:
    data = yaml.safe_load(file)
    print(data)
$ python load_yaml.py
{'country': False}

これに対する解決策は簡単で、ただNOをquoteで囲うだけです。

norway.yaml
contry: 'NO'

なんとなく今回はそのようなYAMLを検知するようなコードを考えてみました。

実際に検知するようなコードを考える

基本的にpyyamlなどは使えないので、YAMLファイルをテキストモードで読み込んで、正規表現でひっかける感じです。

check_norway_problem.py
import re


class YAMLNorwayProblemChecker:
    
    BOOLEAN_REPRESENT_VALUES = {
        "y", "Y", "yes", "Yes", "YES",
        "n", "N", "no", "No", "NO",
        "true", "True", "TRUE",
        "false", "False", "FALSE",
        "on", "On", "ON",
        "off", "Off", "OFF"
    }

    def __init__(self, file_path):
        self.file_path = file_path
        self.raw_lines = self.load_raw_yaml()

    def load_raw_yaml(self):
        
        with open(self.file_path, 'r', encoding='utf-8') as f:
            return f.readlines()

    def check_norway_problem(self):
       
        # 真偽値が入ることが前提のキーを除外
        exclude_keys = {}

        # YAMLのkey: valueパターンを定義
        yaml_pattern = r'^\s*-?\s*(\S+):\s*(\S+)$'

        norway_problem_detected = False

        for i, line in enumerate(self.raw_lines, start=1):
            # 空白削除
            line = line.strip()
            # 空行やコメント行はスキップ
            if not line or line.startswith('#'):
                continue

            # 正規表現でkey: valueのペアを取得
            match = re.match(yaml_pattern, line)
            if match:
                key, value = match.groups()

                # 除外対象のキーをスキップ
                if key in exclude_keys:
                    continue

                # キーが真偽値として解釈されるかチェック
                if key in self.BOOLEAN_REPRESENT_VALUES:
                    print(f"ノルウェー問題が検出されました。\n{i}行目: キー '{key}' を引用符で囲んでください。")
                    norway_problem_detected = True

                # 値が真偽値として解釈されるかチェック
                if value in self.BOOLEAN_REPRESENT_VALUES:
                    print(f"ノルウェー問題が検出されました。\n{i}行目: 値 '{value}' を引用符で囲んでください。")
                    norway_problem_detected = True

        if not norway_problem_detected:
            print("ノルウェー問題は検出されませんでした。")

if __name__ == "__main__":
    yaml_file_path = "example.yaml"
    checker = YAMLNorwayProblemChecker(yaml_file_path)
    checker.check_norway_problem()

example.yaml
country: "NO"
switch: ON
confirm: YES
$ python check_norway_problem.py
ノルウェー問題が検出されました。
2行目: 値 'ON' を引用符で囲んでください。
ノルウェー問題が検出されました。
3行目: 値 'YES' を引用符で囲んでください。

参考

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?