Merge PR #458: Add explicit UTF-8 encoding to config/data file I/O
Authored by shitcoinsherpa. Adds encoding='utf-8' to all text-mode open() calls in gateway/run.py, gateway/config.py, hermes_cli/config.py, hermes_cli/main.py, and hermes_cli/status.py. Prevents encoding errors on Windows where the default locale is not UTF-8. Also fixed 4 additional open() calls in gateway/run.py that were added after the PR branch was created.
This commit is contained in:
commit
36328a996f
6 changed files with 51 additions and 48 deletions
|
|
@ -270,7 +270,7 @@ def load_gateway_config() -> GatewayConfig:
|
||||||
gateway_config_path = Path.home() / ".hermes" / "gateway.json"
|
gateway_config_path = Path.home() / ".hermes" / "gateway.json"
|
||||||
if gateway_config_path.exists():
|
if gateway_config_path.exists():
|
||||||
try:
|
try:
|
||||||
with open(gateway_config_path, "r") as f:
|
with open(gateway_config_path, "r", encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
config = GatewayConfig.from_dict(data)
|
config = GatewayConfig.from_dict(data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -283,7 +283,7 @@ def load_gateway_config() -> GatewayConfig:
|
||||||
import yaml
|
import yaml
|
||||||
config_yaml_path = Path.home() / ".hermes" / "config.yaml"
|
config_yaml_path = Path.home() / ".hermes" / "config.yaml"
|
||||||
if config_yaml_path.exists():
|
if config_yaml_path.exists():
|
||||||
with open(config_yaml_path) as f:
|
with open(config_yaml_path, encoding="utf-8") as f:
|
||||||
yaml_cfg = yaml.safe_load(f) or {}
|
yaml_cfg = yaml.safe_load(f) or {}
|
||||||
sr = yaml_cfg.get("session_reset")
|
sr = yaml_cfg.get("session_reset")
|
||||||
if sr and isinstance(sr, dict):
|
if sr and isinstance(sr, dict):
|
||||||
|
|
@ -441,5 +441,5 @@ def save_gateway_config(config: GatewayConfig) -> None:
|
||||||
gateway_config_path = Path.home() / ".hermes" / "gateway.json"
|
gateway_config_path = Path.home() / ".hermes" / "gateway.json"
|
||||||
gateway_config_path.parent.mkdir(parents=True, exist_ok=True)
|
gateway_config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with open(gateway_config_path, "w") as f:
|
with open(gateway_config_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(config.to_dict(), f, indent=2)
|
json.dump(config.to_dict(), f, indent=2)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ _config_path = _hermes_home / 'config.yaml'
|
||||||
if _config_path.exists():
|
if _config_path.exists():
|
||||||
try:
|
try:
|
||||||
import yaml as _yaml
|
import yaml as _yaml
|
||||||
with open(_config_path) as _f:
|
with open(_config_path, encoding="utf-8") as _f:
|
||||||
_cfg = _yaml.safe_load(_f) or {}
|
_cfg = _yaml.safe_load(_f) or {}
|
||||||
# Top-level simple values (fallback only — don't override .env)
|
# Top-level simple values (fallback only — don't override .env)
|
||||||
for _key, _val in _cfg.items():
|
for _key, _val in _cfg.items():
|
||||||
|
|
@ -316,7 +316,7 @@ class GatewayRunner:
|
||||||
import yaml as _y
|
import yaml as _y
|
||||||
cfg_path = _hermes_home / "config.yaml"
|
cfg_path = _hermes_home / "config.yaml"
|
||||||
if cfg_path.exists():
|
if cfg_path.exists():
|
||||||
with open(cfg_path) as _f:
|
with open(cfg_path, encoding="utf-8") as _f:
|
||||||
cfg = _y.safe_load(_f) or {}
|
cfg = _y.safe_load(_f) or {}
|
||||||
file_path = cfg.get("prefill_messages_file", "")
|
file_path = cfg.get("prefill_messages_file", "")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -354,7 +354,7 @@ class GatewayRunner:
|
||||||
import yaml as _y
|
import yaml as _y
|
||||||
cfg_path = _hermes_home / "config.yaml"
|
cfg_path = _hermes_home / "config.yaml"
|
||||||
if cfg_path.exists():
|
if cfg_path.exists():
|
||||||
with open(cfg_path) as _f:
|
with open(cfg_path, encoding="utf-8") as _f:
|
||||||
cfg = _y.safe_load(_f) or {}
|
cfg = _y.safe_load(_f) or {}
|
||||||
return (cfg.get("agent", {}).get("system_prompt", "") or "").strip()
|
return (cfg.get("agent", {}).get("system_prompt", "") or "").strip()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -375,7 +375,7 @@ class GatewayRunner:
|
||||||
import yaml as _y
|
import yaml as _y
|
||||||
cfg_path = _hermes_home / "config.yaml"
|
cfg_path = _hermes_home / "config.yaml"
|
||||||
if cfg_path.exists():
|
if cfg_path.exists():
|
||||||
with open(cfg_path) as _f:
|
with open(cfg_path, encoding="utf-8") as _f:
|
||||||
cfg = _y.safe_load(_f) or {}
|
cfg = _y.safe_load(_f) or {}
|
||||||
effort = str(cfg.get("agent", {}).get("reasoning_effort", "") or "").strip()
|
effort = str(cfg.get("agent", {}).get("reasoning_effort", "") or "").strip()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -398,7 +398,7 @@ class GatewayRunner:
|
||||||
import yaml as _y
|
import yaml as _y
|
||||||
cfg_path = _hermes_home / "config.yaml"
|
cfg_path = _hermes_home / "config.yaml"
|
||||||
if cfg_path.exists():
|
if cfg_path.exists():
|
||||||
with open(cfg_path) as _f:
|
with open(cfg_path, encoding="utf-8") as _f:
|
||||||
cfg = _y.safe_load(_f) or {}
|
cfg = _y.safe_load(_f) or {}
|
||||||
return cfg.get("provider_routing", {}) or {}
|
return cfg.get("provider_routing", {}) or {}
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -416,7 +416,7 @@ class GatewayRunner:
|
||||||
import yaml as _y
|
import yaml as _y
|
||||||
cfg_path = _hermes_home / "config.yaml"
|
cfg_path = _hermes_home / "config.yaml"
|
||||||
if cfg_path.exists():
|
if cfg_path.exists():
|
||||||
with open(cfg_path) as _f:
|
with open(cfg_path, encoding="utf-8") as _f:
|
||||||
cfg = _y.safe_load(_f) or {}
|
cfg = _y.safe_load(_f) or {}
|
||||||
fb = cfg.get("fallback_model", {}) or {}
|
fb = cfg.get("fallback_model", {}) or {}
|
||||||
if fb.get("provider") and fb.get("model"):
|
if fb.get("provider") and fb.get("model"):
|
||||||
|
|
@ -931,7 +931,7 @@ class GatewayRunner:
|
||||||
_hyg_cfg_path = _hermes_home / "config.yaml"
|
_hyg_cfg_path = _hermes_home / "config.yaml"
|
||||||
if _hyg_cfg_path.exists():
|
if _hyg_cfg_path.exists():
|
||||||
import yaml as _hyg_yaml
|
import yaml as _hyg_yaml
|
||||||
with open(_hyg_cfg_path) as _hyg_f:
|
with open(_hyg_cfg_path, encoding="utf-8") as _hyg_f:
|
||||||
_hyg_data = _hyg_yaml.safe_load(_hyg_f) or {}
|
_hyg_data = _hyg_yaml.safe_load(_hyg_f) or {}
|
||||||
|
|
||||||
# Resolve model name (same logic as run_sync)
|
# Resolve model name (same logic as run_sync)
|
||||||
|
|
@ -1434,7 +1434,7 @@ class GatewayRunner:
|
||||||
current_provider = "openrouter"
|
current_provider = "openrouter"
|
||||||
try:
|
try:
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
with open(config_path) as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
cfg = yaml.safe_load(f) or {}
|
cfg = yaml.safe_load(f) or {}
|
||||||
model_cfg = cfg.get("model", {})
|
model_cfg = cfg.get("model", {})
|
||||||
if isinstance(model_cfg, str):
|
if isinstance(model_cfg, str):
|
||||||
|
|
@ -1525,14 +1525,14 @@ class GatewayRunner:
|
||||||
try:
|
try:
|
||||||
user_config = {}
|
user_config = {}
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
with open(config_path) as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
user_config = yaml.safe_load(f) or {}
|
user_config = yaml.safe_load(f) or {}
|
||||||
if "model" not in user_config or not isinstance(user_config["model"], dict):
|
if "model" not in user_config or not isinstance(user_config["model"], dict):
|
||||||
user_config["model"] = {}
|
user_config["model"] = {}
|
||||||
user_config["model"]["default"] = new_model
|
user_config["model"]["default"] = new_model
|
||||||
if provider_changed:
|
if provider_changed:
|
||||||
user_config["model"]["provider"] = target_provider
|
user_config["model"]["provider"] = target_provider
|
||||||
with open(config_path, 'w') as f:
|
with open(config_path, 'w', encoding="utf-8") as f:
|
||||||
yaml.dump(user_config, f, default_flow_style=False, sort_keys=False)
|
yaml.dump(user_config, f, default_flow_style=False, sort_keys=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"⚠️ Failed to save model change: {e}"
|
return f"⚠️ Failed to save model change: {e}"
|
||||||
|
|
@ -1569,7 +1569,7 @@ class GatewayRunner:
|
||||||
config_path = _hermes_home / 'config.yaml'
|
config_path = _hermes_home / 'config.yaml'
|
||||||
try:
|
try:
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
with open(config_path) as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
cfg = yaml.safe_load(f) or {}
|
cfg = yaml.safe_load(f) or {}
|
||||||
model_cfg = cfg.get("model", {})
|
model_cfg = cfg.get("model", {})
|
||||||
if isinstance(model_cfg, dict):
|
if isinstance(model_cfg, dict):
|
||||||
|
|
@ -1618,7 +1618,7 @@ class GatewayRunner:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
with open(config_path, 'r') as f:
|
with open(config_path, 'r', encoding="utf-8") as f:
|
||||||
config = yaml.safe_load(f) or {}
|
config = yaml.safe_load(f) or {}
|
||||||
personalities = config.get("agent", {}).get("personalities", {})
|
personalities = config.get("agent", {}).get("personalities", {})
|
||||||
else:
|
else:
|
||||||
|
|
@ -1647,7 +1647,7 @@ class GatewayRunner:
|
||||||
if "agent" not in config or not isinstance(config.get("agent"), dict):
|
if "agent" not in config or not isinstance(config.get("agent"), dict):
|
||||||
config["agent"] = {}
|
config["agent"] = {}
|
||||||
config["agent"]["system_prompt"] = new_prompt
|
config["agent"]["system_prompt"] = new_prompt
|
||||||
with open(config_path, 'w') as f:
|
with open(config_path, 'w', encoding="utf-8") as f:
|
||||||
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"⚠️ Failed to save personality change: {e}"
|
return f"⚠️ Failed to save personality change: {e}"
|
||||||
|
|
@ -1731,10 +1731,10 @@ class GatewayRunner:
|
||||||
config_path = _hermes_home / 'config.yaml'
|
config_path = _hermes_home / 'config.yaml'
|
||||||
user_config = {}
|
user_config = {}
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
with open(config_path) as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
user_config = yaml.safe_load(f) or {}
|
user_config = yaml.safe_load(f) or {}
|
||||||
user_config[env_key] = chat_id
|
user_config[env_key] = chat_id
|
||||||
with open(config_path, 'w') as f:
|
with open(config_path, 'w', encoding="utf-8") as f:
|
||||||
yaml.dump(user_config, f, default_flow_style=False)
|
yaml.dump(user_config, f, default_flow_style=False)
|
||||||
# Also set in the current environment so it takes effect immediately
|
# Also set in the current environment so it takes effect immediately
|
||||||
os.environ[env_key] = str(chat_id)
|
os.environ[env_key] = str(chat_id)
|
||||||
|
|
@ -2410,7 +2410,7 @@ class GatewayRunner:
|
||||||
config_path = _hermes_home / 'config.yaml'
|
config_path = _hermes_home / 'config.yaml'
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
import yaml
|
import yaml
|
||||||
with open(config_path, 'r') as f:
|
with open(config_path, 'r', encoding="utf-8") as f:
|
||||||
user_config = yaml.safe_load(f) or {}
|
user_config = yaml.safe_load(f) or {}
|
||||||
platform_toolsets_config = user_config.get("platform_toolsets", {})
|
platform_toolsets_config = user_config.get("platform_toolsets", {})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -2440,7 +2440,7 @@ class GatewayRunner:
|
||||||
_tp_cfg_path = _hermes_home / "config.yaml"
|
_tp_cfg_path = _hermes_home / "config.yaml"
|
||||||
if _tp_cfg_path.exists():
|
if _tp_cfg_path.exists():
|
||||||
import yaml as _tp_yaml
|
import yaml as _tp_yaml
|
||||||
with open(_tp_cfg_path) as _tp_f:
|
with open(_tp_cfg_path, encoding="utf-8") as _tp_f:
|
||||||
_tp_data = _tp_yaml.safe_load(_tp_f) or {}
|
_tp_data = _tp_yaml.safe_load(_tp_f) or {}
|
||||||
_progress_cfg = _tp_data.get("display", {})
|
_progress_cfg = _tp_data.get("display", {})
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -2658,7 +2658,7 @@ class GatewayRunner:
|
||||||
import yaml as _y
|
import yaml as _y
|
||||||
_cfg_path = _hermes_home / "config.yaml"
|
_cfg_path = _hermes_home / "config.yaml"
|
||||||
if _cfg_path.exists():
|
if _cfg_path.exists():
|
||||||
with open(_cfg_path) as _f:
|
with open(_cfg_path, encoding="utf-8") as _f:
|
||||||
_cfg = _y.safe_load(_f) or {}
|
_cfg = _y.safe_load(_f) or {}
|
||||||
_model_cfg = _cfg.get("model", {})
|
_model_cfg = _cfg.get("model", {})
|
||||||
if isinstance(_model_cfg, str):
|
if isinstance(_model_cfg, str):
|
||||||
|
|
@ -3140,7 +3140,7 @@ def main():
|
||||||
config = None
|
config = None
|
||||||
if args.config:
|
if args.config:
|
||||||
import json
|
import json
|
||||||
with open(args.config) as f:
|
with open(args.config, encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
config = GatewayConfig.from_dict(data)
|
config = GatewayConfig.from_dict(data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -757,9 +757,9 @@ def load_config() -> Dict[str, Any]:
|
||||||
|
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
try:
|
try:
|
||||||
with open(config_path) as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
user_config = yaml.safe_load(f) or {}
|
user_config = yaml.safe_load(f) or {}
|
||||||
|
|
||||||
config = _deep_merge(config, user_config)
|
config = _deep_merge(config, user_config)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Warning: Failed to load config: {e}")
|
print(f"Warning: Failed to load config: {e}")
|
||||||
|
|
@ -802,7 +802,7 @@ def save_config(config: Dict[str, Any]):
|
||||||
ensure_hermes_home()
|
ensure_hermes_home()
|
||||||
config_path = get_config_path()
|
config_path = get_config_path()
|
||||||
|
|
||||||
with open(config_path, 'w') as f:
|
with open(config_path, 'w', encoding="utf-8") as f:
|
||||||
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
||||||
# Append commented-out sections for features that are off by default
|
# Append commented-out sections for features that are off by default
|
||||||
# or only relevant when explicitly configured. Skip sections the
|
# or only relevant when explicitly configured. Skip sections the
|
||||||
|
|
@ -1077,7 +1077,7 @@ def set_config_value(key: str, value: str):
|
||||||
user_config = {}
|
user_config = {}
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
try:
|
try:
|
||||||
with open(config_path) as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
user_config = yaml.safe_load(f) or {}
|
user_config = yaml.safe_load(f) or {}
|
||||||
except Exception:
|
except Exception:
|
||||||
user_config = {}
|
user_config = {}
|
||||||
|
|
@ -1105,7 +1105,7 @@ def set_config_value(key: str, value: str):
|
||||||
|
|
||||||
# Write only user config back (not the full merged defaults)
|
# Write only user config back (not the full merged defaults)
|
||||||
ensure_hermes_home()
|
ensure_hermes_home()
|
||||||
with open(config_path, 'w') as f:
|
with open(config_path, 'w', encoding="utf-8") as f:
|
||||||
yaml.dump(user_config, f, default_flow_style=False, sort_keys=False)
|
yaml.dump(user_config, f, default_flow_style=False, sort_keys=False)
|
||||||
|
|
||||||
# Keep .env in sync for keys that terminal_tool reads directly from env vars.
|
# Keep .env in sync for keys that terminal_tool reads directly from env vars.
|
||||||
|
|
|
||||||
|
|
@ -2356,12 +2356,12 @@ For more help on a command:
|
||||||
if not data:
|
if not data:
|
||||||
print(f"Session '{args.session_id}' not found.")
|
print(f"Session '{args.session_id}' not found.")
|
||||||
return
|
return
|
||||||
with open(args.output, "w") as f:
|
with open(args.output, "w", encoding="utf-8") as f:
|
||||||
f.write(_json.dumps(data, ensure_ascii=False) + "\n")
|
f.write(_json.dumps(data, ensure_ascii=False) + "\n")
|
||||||
print(f"Exported 1 session to {args.output}")
|
print(f"Exported 1 session to {args.output}")
|
||||||
else:
|
else:
|
||||||
sessions = db.export_all(source=args.source)
|
sessions = db.export_all(source=args.source)
|
||||||
with open(args.output, "w") as f:
|
with open(args.output, "w", encoding="utf-8") as f:
|
||||||
for s in sessions:
|
for s in sessions:
|
||||||
f.write(_json.dumps(s, ensure_ascii=False) + "\n")
|
f.write(_json.dumps(s, ensure_ascii=False) + "\n")
|
||||||
print(f"Exported {len(sessions)} sessions to {args.output}")
|
print(f"Exported {len(sessions)} sessions to {args.output}")
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,7 @@ def show_status(args):
|
||||||
if jobs_file.exists():
|
if jobs_file.exists():
|
||||||
import json
|
import json
|
||||||
try:
|
try:
|
||||||
with open(jobs_file) as f:
|
with open(jobs_file, encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
jobs = data.get("jobs", [])
|
jobs = data.get("jobs", [])
|
||||||
enabled_jobs = [j for j in jobs if j.get("enabled", True)]
|
enabled_jobs = [j for j in jobs if j.get("enabled", True)]
|
||||||
|
|
@ -283,7 +283,7 @@ def show_status(args):
|
||||||
if sessions_file.exists():
|
if sessions_file.exists():
|
||||||
import json
|
import json
|
||||||
try:
|
try:
|
||||||
with open(sessions_file) as f:
|
with open(sessions_file, encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
print(f" Active: {len(data)} session(s)")
|
print(f" Active: {len(data)} session(s)")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,8 @@ def _prompt_for_sudo_password(timeout_seconds: int = 45) -> str:
|
||||||
|
|
||||||
def read_password_thread():
|
def read_password_thread():
|
||||||
"""Read password with echo disabled. Uses msvcrt on Windows, /dev/tty on Unix."""
|
"""Read password with echo disabled. Uses msvcrt on Windows, /dev/tty on Unix."""
|
||||||
|
tty_fd = None
|
||||||
|
old_attrs = None
|
||||||
try:
|
try:
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
import msvcrt
|
import msvcrt
|
||||||
|
|
@ -213,28 +215,29 @@ def _prompt_for_sudo_password(timeout_seconds: int = 45) -> str:
|
||||||
new_attrs = termios.tcgetattr(tty_fd)
|
new_attrs = termios.tcgetattr(tty_fd)
|
||||||
new_attrs[3] = new_attrs[3] & ~termios.ECHO
|
new_attrs[3] = new_attrs[3] & ~termios.ECHO
|
||||||
termios.tcsetattr(tty_fd, termios.TCSAFLUSH, new_attrs)
|
termios.tcsetattr(tty_fd, termios.TCSAFLUSH, new_attrs)
|
||||||
try:
|
chars = []
|
||||||
chars = []
|
while True:
|
||||||
while True:
|
b = os.read(tty_fd, 1)
|
||||||
b = os.read(tty_fd, 1)
|
if not b or b in (b"\n", b"\r"):
|
||||||
if not b or b in (b"\n", b"\r"):
|
break
|
||||||
break
|
chars.append(b)
|
||||||
chars.append(b)
|
result["password"] = b"".join(chars).decode("utf-8", errors="replace")
|
||||||
result["password"] = b"".join(chars).decode("utf-8", errors="replace")
|
|
||||||
finally:
|
|
||||||
try:
|
|
||||||
termios.tcsetattr(tty_fd, termios.TCSAFLUSH, old_attrs)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
os.close(tty_fd)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
except (EOFError, KeyboardInterrupt, OSError):
|
except (EOFError, KeyboardInterrupt, OSError):
|
||||||
result["password"] = ""
|
result["password"] = ""
|
||||||
except Exception:
|
except Exception:
|
||||||
result["password"] = ""
|
result["password"] = ""
|
||||||
finally:
|
finally:
|
||||||
|
if tty_fd is not None and old_attrs is not None:
|
||||||
|
try:
|
||||||
|
import termios as _termios
|
||||||
|
_termios.tcsetattr(tty_fd, _termios.TCSAFLUSH, old_attrs)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if tty_fd is not None:
|
||||||
|
try:
|
||||||
|
os.close(tty_fd)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
result["done"] = True
|
result["done"] = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue