Сообщить об ошибке.

Безопасная загрузка классов Python из документа YAML

При помощи модуля PyYAML, используя в документе YAML тег !!python/object, могут быть загружены и созданы экземпляры классов Python.

import yaml
# имеющийся класс
class Hero():
    def __init__(self, name, hp, sp):
        self.name = name
        self.hp = hp
        self.sp = sp
    def __repr__(self):
        return(f"{self.__class__.__name__}(name={self.name}, hp={self.hp}, sp={self.sp})")

# документ YAML с экземпляром класса `Hero()`
doc_yml = """
!!python/object:__main__.Hero
name: Welthyr Syxgon
hp: 1200
sp: 0
"""

# загружаем экземпляр класса `Hero()`
new_hero = yaml.load(doc_yml, Loader=yaml.Loader)
print(new_hero)
# Hero(name=Welthyr Syxgon, hp=1200, sp=0)

Обратите внимание, что возможность создания произвольного объекта Python может быть опасной, если документ YAML получен из ненадежного источника, такого как Интернет. Функция yaml.safe_load() ограничивает эту возможность простыми объектами Python, такими как целые числа или списки.

Безопасная загрузка пользовательских классов Python.

Объект python может быть помечен как безопасный и, таким образом, распознается yaml.safe_load(). Для этого, создаваемый объект унаследуйте от yaml.YAMLObject и явно установите его свойство класса yaml_loader на yaml.SafeLoader.

Например:

import yaml
# имеющийся класс
class Monster(yaml.YAMLObject):
    yaml_tag = '!Monster'
    yaml_loader = yaml.SafeLoader

    def __init__(self, name, hp, ac, attacks):
        self.name = name
        self.hp = hp
        self.ac = ac
        self.attacks = attacks
        
    def __repr__(self):
        return (f"{self.__class__.__name__}(name={self.name},"
                f" hp={self.hp}, ac={self.ac}, attacks={self.attacks})")

# документ YAML с экземпляром класса `Monster()`
yml_doc = """
--- !Monster
name: Cave spider
hp: [2,6]    # 2d6
ac: 16
attacks: [BITE, HURT]
"""

# загружаем экземпляр `Monster()` из документ 
# YAML в безопасном режиме
monster1 = yaml.safe_load(yml_doc)
# смотрим
print(monster1)
# Monster(name=Cave spider, hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])

Другой способ безопасной загрузки/выгрузки объектов из документа YAML.

Преимущество здесь в том, что над (де)сериализацией класса есть полный контроль. Это означает, что класс не получит случайный исполняемый код по сети и не запустит его. И это же является недостатком, т.е. нужно делать намного больше работы для написания классов.

import yaml

class User():
    def __init__(self, name, surname):
       self.name= name
       self.surname= surname

    def yaml(self):
       return yaml.dump(self.__dict__)

    @staticmethod
    def load(data):
       values = yaml.safe_load(data)
       return User(values["name"], values["surname"])

user = User('spam', 'eggs')
serialized_user = user.yaml()
print(f"serialized_user:  {serialized_user.strip()}")
# serialized_user:  name: spam
# surname: eggs

# Network
deserialized_user = User.load(serialized_user)
print(f"name: {deserialized_user.name}, sname: {deserialized_user.surname}")
# name: spam, sname: eggs