ruamel.yaml 可以支持像 "num: !!float 4" 这样的类型描述符吗?

Could ruamel.yaml support type descriptor like "num: !!float 4"?

我正在学习使用 ruamel.yaml,我想知道它是否支持像“num: !!float 4”这样的原始 YAML 类型描述符? 文件如下:

num: !!float 4

我尝试导入这样的文件,但遇到错误:

---------------------------------------------------------------------------<br>
ValueError                                Traceback (most recent call last)
Input In [22], in <cell line: 2>()
      1 from ruamel import yaml
      2 with open("net.yaml", "r", encoding="utf-8") as yaml_file:
----> 3     yaml_dict = yaml.round_trip_load(yaml_file)
      4 yaml_dict

...

File ~/software/python/anaconda/anaconda3/envs/conda-general/lib/python3.10/site-packages/ruamel/yaml/constructor.py:1469, in RoundTripConstructor.construct_mapping(self, node, maptyp, deep)
   1462     if not isinstance(key, Hashable):
   1463         raise ConstructorError(
   1464             'while constructing a mapping',
   1465             node.start_mark,
   1466             'found unhashable key',
   1467             key_node.start_mark,
   1468         )
-> 1469 value = self.construct_object(value_node, deep=deep)
   1470 if self.check_mapping_key(node, key_node, maptyp, key, value):
   1471     if key_node.comment and len(key_node.comment) > 4 and key_node.comment[4]:

File ~/software/python/anaconda/anaconda3/envs/conda-general/lib/python3.10/site-packages/ruamel/yaml/constructor.py:146, in BaseConstructor.construct_object(self, node, deep)
    142     # raise ConstructorError(
    143     #     None, None, 'found unconstructable recursive node', node.start_mark
    144     # )
    145 self.recursive_objects[node] = None
--> 146 data = self.construct_non_recursive_object(node)
    148 self.constructed_objects[node] = data
    149 del self.recursive_objects[node]

File ~/software/python/anaconda/anaconda3/envs/conda-general/lib/python3.10/site-packages/ruamel/yaml/constructor.py:181, in BaseConstructor.construct_non_recursive_object(self, node, tag)
    179             constructor = self.__class__.construct_mapping
    180 if tag_suffix is None:
--> 181     data = constructor(self, node)
    182 else:
    183     data = constructor(self, tag_suffix, node)

File ~/software/python/anaconda/anaconda3/envs/conda-general/lib/python3.10/site-packages/ruamel/yaml/constructor.py:1271, in RoundTripConstructor.construct_yaml_float(self, node)
   1259     return ScalarFloat(
   1260         sign * float(value_s),
   1261         width=width,
   (...)
   1268         anchor=node.anchor,
   1269     )
   1270 width = len(value_so)
-> 1271 prec = value_so.index('.')  # you can use index, this would not be float without dot
   1272 lead0 = leading_zeros(value_so)
   1273 return ScalarFloat(
   1274     sign * float(value_s),
   1275     width=width,
   (...)
   1279     anchor=node.anchor,
   1280 )

ValueError: substring not found

为什么会出现此错误,如何消除它?

这是 ruamel.yaml<=0.17.21 中的错误。对违规行 (1271) 的评论说

# you can use index, this would not be float without dot

显然该评论的作者不知道他在说什么,就像您的情况一样,当使用 !!float 4 时,您有一个没有点的浮点数...

通过在第 1271 行用 find 替换 index 来“修复”这个问题是微不足道的,这样做会加载您的文档并且您可以转储数据。 但是相应的转储表示不处理将浮点数输出为 4.0,删除标签。

您可以通过注册一个更简单的 float 构造函数(例如来自 SafeLoader 的简单构造函数)临时解决此问题,尽管这会影响所有 float:

import sys
import ruamel.yaml

yaml_str = """\
num: !!float 4
"""
    

yaml = ruamel.yaml.YAML()
yaml.constructor.add_constructor(
    'tag:yaml.org,2002:float', ruamel.yaml.constructor.SafeConstructor.construct_yaml_float
)
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)

给出:

num: 4.0