【问题标题】:Unexpected behavior when using YAML Storage with TinyDB将 YAML 存储与 TinyDB 一起使用时出现意外行为
【发布时间】:2016-05-17 15:33:42
【问题描述】:

我正在使用 TinyDB 作为一个小型 CLI 实用程序来管理个人文档草稿。数据库存储每个草稿的元数据;该文件应该是人工可编辑的(以便我可以手动添加详细信息),因此我想使用 YAML over JSON 作为格式。

我实现了 YamlStorage 类的子类化 storages.Storage,如 TinyDB 文档中所述:

class TestYamlStorage(Storage):
    """
    Store the data in a YAML file.
    Written following the example at http://tinydb.readthedocs.io/en/latest/extend.html#write-a-custom-storage
    """
    def __init__(self, filename):  # (1)
        super().__init__()
        self.filename = filename
        touch(filename)

    def read(self):
        with open(self.filename) as handle:
            try:
                data = yaml.load(handle.read())
                return data
            except yaml.YAMLError:
                return None  # (3)

    def write(self, data):
        print('writing data: {}'.format(data))
        with open(self.filename, 'w') as handle:
                yaml.dump(data, handle)


    def close(self):  # (4)
        pass

使用insert_multiple插入一个元素或同时插入多个元素时一切正常:

db = TinyDB('db.yaml', storage=TestYamlStorage)
dicts = [
    dict(name='Homer', age=38),
    dict(name='Marge', age=34),
    dict(name='Bart', age=10)
]

# this works as expected
db.insert_multiple(dicts)

生成的db.yaml

_default:
  1: {age: 38, name: Homer}
  2: {age: 34, name: Marge}
  3: {age: 10, name: Bart}

但是,当使用insert 多次插入元素时,生成的 YAML 文件会有所不同:

db = TinyDB('db.yaml', storage=TestYamlStorage)

db.insert(dict(name='Homer', age=38))
db.insert(dict(name='Bart', age=10))

db.yaml:

_default:
  1: !!python/object/new:tinydb.database.Element
    dictitems: {age: 38, name: Homer}
    state: {eid: 1}
  2: {age: 10, name: Bart}

这种格式的数据(除了看起来更乱)似乎与yaml.safe_load不兼容(调用db.all()返回[])。我的解释是,YAML 序列化过程在某种程度上“过于急切”,即Element 实例被写入db.yaml 而不是底层数据。

我的代码有问题吗?我尝试使用不同的 YAML 模块(ruamel.yaml)来摆弄 PyYAML 选项,并从默认 JSONStorage 复制创建第二个 YamlStorage 类,但没有任何区别。

版本信息:Python 3.4.3、TinyDB 3.2.0、PyYAML 3.11。我发布了一个可运行的 MWE,其中包含所有导入 here

编辑

在@Anthon 的建议之后,我尝试在转储到文件之前立即将 YAML 输出打印到 sys.stdout。在这种情况下也重现了该问题。见notebook

【问题讨论】:

    标签: python yaml pyyaml tinydb


    【解决方案1】:

    当您更新现有的“数据库”时,您会检索到一个 database.Element,其中包括(如您在第二个 YAML 文件中所见)状态信息。

    再次保存时,您不会保存dict,而是此Element 的一个实例,它是dict 的子类,为此ruamel.yaml(和PyYAML)需要同时存储@987654326 @(dict 的键值对)和 state(表示该属性及其值的字典)。

    在编写之前将您的元素显式转换为dict 应该可以解决问题:

        def write(self, data):
            print('writing data: {}'.format(data))
            with open(self.filename, 'w') as handle:
                    yaml.dump(dict(data), handle)
        #                      ^^^^    ^
    

    【讨论】:

    • 我尝试了你的建议,但不幸的是它似乎没有任何区别。如果相关,print(type(data)) 在调用yaml.dump 之前返回<class 'dict'>
    • 我宁愿用 stream=sys.stdout 做一个 YAML 转储,这样你就可以看到 YAML 认为它得到了什么。
    • 我添加了一个笔记本链接到我的问题,转储到sys.stdout
    猜你喜欢
    • 1970-01-01
    • 2019-01-23
    • 2021-04-20
    • 2021-01-30
    • 2017-04-29
    • 2014-07-12
    • 1970-01-01
    • 2019-04-04
    • 2018-06-16
    相关资源
    最近更新 更多