From 210958d0459a1febe6b8f737611d7389676a4894 Mon Sep 17 00:00:00 2001 From: Hirokuni Kitahara Date: Tue, 24 Oct 2023 11:33:20 +0900 Subject: [PATCH] add workaround for a multi thread issue of ruamel.yaml (#200) * use safe ruamel.yaml instance Signed-off-by: hirokuni-kitahara * add retrying to yaml.dump Signed-off-by: hirokuni-kitahara --------- Signed-off-by: hirokuni-kitahara --- ansible_risk_insight/yaml.py | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/ansible_risk_insight/yaml.py b/ansible_risk_insight/yaml.py index 82c1e0da..99e14b2d 100644 --- a/ansible_risk_insight/yaml.py +++ b/ansible_risk_insight/yaml.py @@ -17,14 +17,15 @@ import io from contextvars import ContextVar from ruamel.yaml import YAML +from ruamel.yaml.emitter import EmitterError _yaml: ContextVar[YAML] = ContextVar("yaml") -def _set_yaml(): - if not _yaml.get(None): - yaml = YAML(typ="rt", pure=True) +def _set_yaml(force=False): + if not _yaml.get(None) or force: + yaml = YAML(typ="safe", pure=True) yaml.default_flow_style = False yaml.preserve_quotes = True yaml.allow_duplicate_keys = True @@ -53,9 +54,30 @@ def load(stream: any): return yaml.load(stream) +# `ruamel.yaml` has a bug around multi-threading, and its YAML() instance could be broken +# while concurrent dump() operation. So we try retrying if the specific error occurs. +# Bug details: https://sourceforge.net/p/ruamel-yaml/tickets/367/ def dump(data: any): _set_yaml() - yaml = _yaml.get() - output = io.StringIO() - yaml.dump(data, output) - return output.getvalue() + retry = 2 + err = None + result = None + for i in range(retry): + try: + yaml = _yaml.get() + output = io.StringIO() + yaml.dump(data, output) + result = output.getvalue() + except EmitterError as exc: + err = exc + except Exception: + raise + if err: + if i < retry - 1: + _set_yaml(force=True) + err = None + else: + raise err + else: + break + return result