You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

字典转YAML对象引号丢失:Stubby配置文件生成异常求助

解决PyYAML safe_dump移除Stubby配置所需引号的问题

我明白你遇到的麻烦了——Stubby要求appdata_dir和tls_auth_name的字符串值必须带双引号才能正常工作,但PyYAML的safe_dump默认会把这些不必要的引号去掉,导致配置失效。下面给你两个可行的解决方案:

方案1:给PyYAML加自定义代表器,强制特定键带引号

我们可以通过重写PyYAML的字符串序列化逻辑,让它在遇到指定的键时,自动给对应的值加上双引号。修改你的代码如下:

import yaml
from yaml.representer import Representer
import logging
import json

# 定义需要强制保留引号的键
REQUIRE_QUOTES = {'appdata_dir', 'tls_auth_name'}

def quoted_str_representer(dumper, value):
    # 检查当前正在序列化的键是否在需要引号的列表里
    if hasattr(dumper, 'key') and dumper.key in REQUIRE_QUOTES:
        return dumper.represent_scalar('tag:yaml.org,2002:str', value, style='"')
    # 其他字符串保持默认处理
    return dumper.represent_scalar('tag:yaml.org,2002:str', value)

# 注册自定义的代表器
yaml.add_representer(str, quoted_str_representer, Representer)

def get_dns_config(dns_option) -> str:
    with open('dns.yml', 'r') as f:
        data = yaml.safe_load(f)
    with open('dns_template.yml', 'r') as f:
        template = yaml.safe_load(f)
        
    template['upstream_recursive_servers'] = data[dns_option]['servers']

    logging.debug(json.dumps(template, indent=4))
    yaml_output = yaml.safe_dump(template, default_flow_style=False, indent=2)
    logging.debug(yaml_output)
    return yaml_output

这个方法的核心是拦截字符串的序列化过程,当检测到目标键时,强制使用双引号包裹值。

方案2:改用ruamel.yaml(更省心的选择)

PyYAML在处理这类格式细节上确实不够灵活,而ruamel.yaml是它的增强版,专门为保留YAML格式细节设计,用它来解决这个问题会更简单。

首先安装库:

pip install ruamel.yaml

然后修改你的代码:

from ruamel.yaml import YAML
from ruamel.yaml.scalarstring import DoubleQuotedScalarString
import logging
import json

def get_dns_config(dns_option) -> str:
    yaml = YAML()
    yaml.indent(mapping=2, sequence=4, offset=2)
    yaml.default_flow_style = False

    with open('dns.yml', 'r') as f:
        data = yaml.load(f)
    with open('dns_template.yml', 'r') as f:
        template = yaml.load(f)
        
    # 给新增的tls_auth_name值加上双引号标记
    for server in data[dns_option]['servers']:
        server['tls_auth_name'] = DoubleQuotedScalarString(server['tls_auth_name'])
    template['upstream_recursive_servers'] = data[dns_option]['servers']
    
    # 如果模板里的appdata_dir加载后没有引号,也手动标记
    template['appdata_dir'] = DoubleQuotedScalarString(template['appdata_dir'])

    logging.debug(json.dumps(template, indent=4))
    
    # 生成YAML输出
    output = []
    yaml.dump(template, output)
    yaml_output = ''.join(output)
    logging.debug(yaml_output)
    return yaml_output

用DoubleQuotedScalarString包装字符串后,ruamel.yaml在输出时会自动保留双引号,完全符合Stubby的要求。

最终效果验证

无论用哪个方案,你都会得到符合要求的YAML输出:

resolution_type: GETDNS_RESOLUTION_STUB
dns_transport_list:
  - GETDNS_TRANSPORT_TLS
tls_authentication: GETDNS_AUTHENTICATION_REQUIRED
tls_query_padding_blocksize: 128
edns_client_subnet_private: 1
round_robin_upstreams: 1
idle_timeout: 10000
listen_addresses:
  - 10.7.0.1
appdata_dir: "/var/cache/stubby"
upstream_recursive_servers:
  - address_data: 185.228.168.168
    tls_auth_name: "family-filter-dns.cleanbrowsing.org"
  - address_data: 185.228.169.168
    tls_auth_name: "family-filter-dns.cleanbrowsing.org"

内容的提问来源于stack exchange,提问作者karnage

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 18:21:00