- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 ruamel.yaml 来解析复杂的 YAML 文档,其中某些标记的节点需要特殊处理。按照已发布示例的建议,我使用 add_multi_constructor
注入(inject)自定义解析逻辑。问题是我需要根据外部状态动态更改注入(inject)的逻辑,但是像 add_multi_constructor
这样的装饰方法修改了全局状态,这在逻辑上不相关的实例之间引入了 Not Acceptable 耦合。这是 MWE:
import ruamel.yaml
def get_loader(parameter):
def construct_node(constructor: ruamel.yaml.Constructor, tag: str, node: ruamel.yaml.Node):
return parameter(tag.lstrip("!"), str(node.value))
loader = ruamel.yaml.YAML()
loader.constructor.add_multi_constructor("", construct_node)
return loader
foo = get_loader(lambda tag, node: f"foo: {tag}, {node}")
bar = get_loader(lambda tag, node: f"bar: {tag}, {node}")
print(foo.load("!abc 123"), bar.load("!xyz 456"), sep="\n")
输出:
bar: abc, 123
bar: xyz, 456
预期:
foo: abc, 123
bar: xyz, 456
我做了以下解决方法,动态创建新类实例以打破耦合:
def get_loader(parameter):
def construct_node(constructor: ruamel.yaml.Constructor, tag: str, node: ruamel.yaml.Node):
return parameter(tag.lstrip("!"), str(node.value))
# Create a new class to prevent state sharing through class attributes.
class ConstructorWrapper(ruamel.yaml.constructor.RoundTripConstructor):
pass
loader = ruamel.yaml.YAML()
loader.Constructor = ConstructorWrapper
loader.constructor.add_multi_constructor("", construct_node)
return loader
我的问题是:
我是否在滥用该库? 全局效果是一个巨大的危险信号,表明我未正确使用 API,但该库缺少任何 API 文档,所以我不确定是什么将是正确的方法。
从 API 损坏的角度来看它是否安全?由于没有记录在案的 API,因此我不确定将其投入生产是否安全。
最佳答案
IMO 你没有滥用这个库,只是在解决它当前的缺点/不完整性。
在ruamel.yaml
得到带有YAML()
实例的API之前,它有函数基于 PyYAML API 的一些扩展,以及其他 PyYAML 的问题必须在一个类似不自然的方式。例如。我恢复到拥有可以调用其实例的类(使用 __call__()
) 然后可以将哪些方法更改为只能访问从文档解析的 YAML 文档版本(因为 ruamel.yaml 支持 YAML1.2 和 1.1 以及 PyYAML 仅(部分)支持 1.1)。
但在 ruamel.yaml 的 YAML()
实例之下,并不是所有的都得到了改进。代码继承自 PyYAML 存储各种构造函数的信息在 class 属性中作为查找表(在 yaml_constructor
上 yaml_multi_constructor
),并且 ruamel.yaml 仍然这样做(作为完整的旧PyYAML-escque API 实际上仍然存在,只有 0.17 版才有 future 弃用警告)。
到目前为止,您的方法很有趣,因为您这样做:
loader.constructor.add_multi_constructor("", construct_node)
代替:
loader.Constructor.add_multi_constructor("", construct_node)
(你可能知道 loader.constructor
是一个实例化的属性 loader.Constructor
,如果有必要,但这个答案的其他读者可能不会)
甚至:
def get_loader(parameter):
def construct_node(constructor: ruamel.yaml.Constructor, tag: str, node: ruamel.yaml.Node):
return parameter(tag.lstrip("!"), str(node.value))
# Create a new class to prevent state sharing through class attributes.
class ConstructorWrapper(ruamel.yaml.constructor.RoundTripConstructor):
pass
ConstructorWrapper.add_multi_constructor("", construct_node)
loader = ruamel.yaml.YAML()
loader.Constructor = ConstructorWrapper
return loader
您的代码有效,是因为构造函数存储在类属性中,因为 .add_multi_constructor()
是类方法。
因此,从 API 破坏的角度来看,您所做的并不完全安全。 ruamel.yaml 不是版本1.0,并且(API)更改可能会破坏您的代码次要版本号更改。你应该相应地设置你的版本依赖您的生产代码(例如 ruamel.yaml<0.18
),并仅在使用具有新次要版本号的 ruamel.yaml 版本进行测试后更新该次要编号。
可以通过更新透明地改变类属性的使用类方法 add_constructor()
和 add_multi_constructor()
为“正常”方法并在 __init__()
中完成查找表的初始化。调用实例的两个示例:
loader.constructor.add_multi_constructor("", construct_node)
会得到想要的结果,但是 ruamel.yaml 的行为不会改变在类上调用 add_multi_constructor
时使用:
loader.Constructor.add_multi_constructor("", construct_node)
但是改变类方法 add_constructor()
和 add_multi_constructor()
以这种方式影响那里的所有代码,恰好提供了一个实例而不是类(并且说代码对结果没问题)。
更有可能是两个新的实例方法将添加到 Constructor
类和 YAML()
实例,并且类方法将被淘汰或更改为检查类而不是实例在带有警告的弃用期之后传入(从 PyYAML 继承的全局函数 add_constructor()
和 add_multi_constructor()
也是如此)。
主要的建议,除了将你的生产代码固定在次要的版本号,是为了确保你的测试代码显示 PendingDeprecationWarning
。如果您使用的是 pytest
,则为 the case bydefault。这应该给你足够的时间来调整你的代码以适应警告推荐。
如果 ruamel.yaml 的作者不再偷懒,他可能会提供此类 API 添加/更改的一些文档。
import ruamel.yaml
import types
import inspect
class MyConstructor(ruamel.yaml.constructor.RoundTripConstructor):
_cls_yaml_constructors = {}
_cls_yaml_multi_constructors = {}
def __init__(self, *args, **kw):
self._yaml_constructors = {
'tag:yaml.org,2002:null': self.__class__.construct_yaml_null,
'tag:yaml.org,2002:bool': self.__class__.construct_yaml_bool,
'tag:yaml.org,2002:int': self.__class__.construct_yaml_int,
'tag:yaml.org,2002:float': self.__class__.construct_yaml_float,
'tag:yaml.org,2002:binary': self.__class__.construct_yaml_binary,
'tag:yaml.org,2002:timestamp': self.__class__.construct_yaml_timestamp,
'tag:yaml.org,2002:omap': self.__class__.construct_yaml_omap,
'tag:yaml.org,2002:pairs': self.__class__.construct_yaml_pairs,
'tag:yaml.org,2002:set': self.__class__.construct_yaml_set,
'tag:yaml.org,2002:str': self.__class__.construct_yaml_str,
'tag:yaml.org,2002:seq': self.__class__.construct_yaml_seq,
'tag:yaml.org,2002:map': self.__class__.construct_yaml_map,
None: self.__class__.construct_undefined
}
self._yaml_constructors.update(self._cls_yaml_constructors)
self._yaml_multi_constructors = self._cls_yaml_multi_constructors.copy()
super().__init__(*args, **kw)
def construct_non_recursive_object(self, node, tag=None):
# type: (Any, Optional[str]) -> Any
constructor = None # type: Any
tag_suffix = None
if tag is None:
tag = node.tag
if tag in self._yaml_constructors:
constructor = self._yaml_constructors[tag]
else:
for tag_prefix in self._yaml_multi_constructors:
if tag.startswith(tag_prefix):
tag_suffix = tag[len(tag_prefix) :]
constructor = self._yaml_multi_constructors[tag_prefix]
break
else:
if None in self._yaml_multi_constructors:
tag_suffix = tag
constructor = self._yaml_multi_constructors[None]
elif None in self._yaml_constructors:
constructor = self._yaml_constructors[None]
elif isinstance(node, ScalarNode):
constructor = self.__class__.construct_scalar
elif isinstance(node, SequenceNode):
constructor = self.__class__.construct_sequence
elif isinstance(node, MappingNode):
constructor = self.__class__.construct_mapping
if tag_suffix is None:
data = constructor(self, node)
else:
data = constructor(self, tag_suffix, node)
if isinstance(data, types.GeneratorType):
generator = data
data = next(generator)
if self.deep_construct:
for _dummy in generator:
pass
else:
self.state_generators.append(generator)
return data
def get_args(*args, **kw):
if kw:
raise NotImplementedError('can currently only handle positional arguments')
if len(args) == 2:
return MyConstructor, args[0], args[1]
else:
return args[0], args[1], args[2]
def add_constructor(self, tag, constructor):
self, tag, constructor = MyConstructor.get_args(*args, **kw)
if inspect.isclass(self):
self._cls_yaml_constructors[tag] = constructor
return
self._yaml_constructors[tag] = constructor
def add_multi_constructor(*args, **kw): # self, tag_prefix, multi_constructor):
self, tag_prefix, multi_constructor = MyConstructor.get_args(*args, **kw)
if inspect.isclass(self):
self._cls_yaml_multi_constructors[tag_prefix] = multi_constructor
return
self._yaml_multi_constructors[tag_prefix] = multi_constructor
def get_loader_org(parameter):
def construct_node(constructor: ruamel.yaml.Constructor, tag: str, node: ruamel.yaml.Node):
return parameter(tag.lstrip("!"), str(node.value))
loader = ruamel.yaml.YAML()
loader.Constructor = MyConstructor
loader.constructor.add_multi_constructor("", construct_node)
return loader
foo = get_loader_org(lambda tag, node: f"foo: {tag}, {node}")
bar = get_loader_org(lambda tag, node: f"bar: {tag}, {node}")
print('>org<', foo.load("!abc 123"), bar.load("!xyz 456"), sep="\n")
def get_loader_instance(parameter):
def construct_node(constructor: ruamel.yaml.Constructor, tag: str, node: ruamel.yaml.Node):
return parameter(tag.lstrip("!"), str(node.value))
# Create a new class to prevent state sharing through class attributes.
class ConstructorWrapper(MyConstructor):
pass
loader = ruamel.yaml.YAML()
loader.Constructor = ConstructorWrapper
loader.constructor.add_multi_constructor("", construct_node)
return loader
foo = get_loader_instance(lambda tag, node: f"foo: {tag}, {node}")
bar = get_loader_instance(lambda tag, node: f"bar: {tag}, {node}")
print('>instance<', foo.load("!abc 123"), bar.load("!xyz 456"), sep="\n")
def get_loader_cls(parameter):
def construct_node(constructor: ruamel.yaml.Constructor, tag: str, node: ruamel.yaml.Node):
return parameter(tag.lstrip("!"), str(node.value))
# Create a new class to prevent state sharing through class attributes.
class ConstructorWrapper(MyConstructor):
pass
loader = ruamel.yaml.YAML()
loader.Constructor = ConstructorWrapper
loader.Constructor.add_multi_constructor("", construct_node)
# ^ using the virtual class method
return loader
foo = get_loader_cls(lambda tag, node: f"foo: {tag}, {node}")
bar = get_loader_cls(lambda tag, node: f"bar: {tag}, {node}")
print('>cls<', foo.load("!abc 123"), bar.load("!xyz 456"), sep="\n")
给出:
>org<
foo: abc, 123
bar: xyz, 456
>instance<
foo: abc, 123
bar: xyz, 456
>cls<
bar: abc, 123
bar: xyz, 456
关于python - 在 ruamel.yaml 中使用自定义构造函数时如何避免全局状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67041211/
我似乎无法弄清楚如何使用 Ruamel.yaml 在数据之间添加一个空行。 假设我有数据: --- a: 1 b: 2 我需要添加到这一点,以便我将有: --- a: 1 b: 2 c: 3 我知道空
我正在使用 Ruamel Python 库以编程方式编辑人工编辑的 YAML 文件。 我很难理解如何将评论插入结构化数据。 我有一些数据: a: b: banana c: apple d:
我正在使用 Ruamel Python 库以编程方式编辑人工编辑的 YAML 文件。源文件具有按字母顺序排序的键。 我不确定这是一个基本的 Python 问题还是一个 Ruamel 问题,但是我尝试对
如果我有多个引用,当我使用 Python 中的 ruaml.yaml 将它们写入 YAML 文件时,我得到: helptexts: - &help-one help_text: |
我有一个使用 ruamel.yaml 和 ruamel.yaml.jinja2 一段时间的 docker 容器。最近它开始失败了。以下 dockerfile 重现了该问题: FROM ubuntu R
这里是python版本: Python 3.6.5 我想安装 ruamel: pip3 install ruamel 但我无法安装它: (venvpython3) d3alg@ubuntu-59:/$
我跑了一个 pip install ruamel.yaml 当我尝试做的时候 import ruamel.yaml 我得到一个错误: ImportError: No module named ruam
ruamel.yaml 文档很少,我在这里没有找到我需要的响应。 my_file.yaml 的内容: desc: "blahblahblah" 问题1:如何(以编程方式)将“size”添加为与“des
我需要对带有注释的 YAML 文件进行排序。我正在使用 ruamel.yaml 库来保留 YAML 中的注释,但是当我对项目进行排序时,注释被放置在错误的位置。 people = """\ # man
我希望使用 ruamel.yaml 对大型人工编辑的 YAML 文件执行一些自动编辑。 输入文件包含合并键,如下所示: foo: &foo color: red bar: name: qux
我想在我的项目中使用 Ruamel YAML,我使用代理,所以我下载了 来自https://pypi.python.org/pypi/ruamel.yaml的gzip包并安装它 pip install
我正在尝试使用 ruamel.yaml 使用 python 动态修改 AWS CloudFormation 模板。我添加了以下代码以使 safe_load 与 CloudFormation 函数(例如
在哪里可以找到 Python 包的完整文档 ruamel.yaml哪个加载和转储 YAML 文件? Python Package Index 仅给出了如何使用注释的示例。我正在尝试了解如何整体使用该包
我有两个问题! 有没有办法防止 round_trip_dump 甚至只是常规转储在句子中间断行?每当我的 YAML 文件中有一个长句子(即描述),并且我使用脚本来更改一些内容时,它就会断行并破坏我的文
我想转储一个包含嵌套列表组件的字典,其中列表的每一行都在自己的行上。我还想保持字典顺序。 这是一个 MWE: import sys from ruamel import yaml from ruame
MWE: import sys from ruamel.yaml import YAML yaml = YAML(typ='safe') yaml.default_flow_style = False
使用 Python 3.4.7,ruamel.yaml 版本 0.15.35,以下内容对我不起作用: import sys import enum import ruamel.yaml from ru
我正在使用 Ruamel 来保留人工编辑的 YAML 文件中的引用样式。 我有示例输入数据: --- a: '1' b: "2" c: 3 我使用以下方式读入数据: def read_file(f):
我正在使用 ruamel.yaml 来解析复杂的 YAML 文档,其中某些标记的节点需要特殊处理。按照已发布示例的建议,我使用 add_multi_constructor 注入(inject)自定义解
给出以下代码: from ruamel.yaml import YAML yaml = YAML() with open(filename) as f: z = yaml.load(f) ya
我是一名优秀的程序员,十分优秀!