feat: use tomli-w for config dumps (#103)
This commit is contained in:
@@ -18,6 +18,7 @@ dependencies = [
|
|||||||
"rich>=14.2.0",
|
"rich>=14.2.0",
|
||||||
"structlog>=25.5.0",
|
"structlog>=25.5.0",
|
||||||
"sulguk>=0.11.1",
|
"sulguk>=0.11.1",
|
||||||
|
"tomli-w>=1.2.0",
|
||||||
"typer>=0.21.0",
|
"typer>=0.21.0",
|
||||||
"watchfiles>=0.21.0",
|
"watchfiles>=0.21.0",
|
||||||
]
|
]
|
||||||
|
|||||||
+7
-61
@@ -5,6 +5,8 @@ from dataclasses import dataclass, field
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
import tomli_w
|
||||||
|
|
||||||
HOME_CONFIG_PATH = Path.home() / ".takopi" / "takopi.toml"
|
HOME_CONFIG_PATH = Path.home() / ".takopi" / "takopi.toml"
|
||||||
|
|
||||||
|
|
||||||
@@ -92,68 +94,12 @@ class ProjectsConfig:
|
|||||||
return tuple(self.chat_map.keys())
|
return tuple(self.chat_map.keys())
|
||||||
|
|
||||||
|
|
||||||
def _toml_escape(value: str) -> str:
|
|
||||||
return value.replace("\\", "\\\\").replace('"', '\\"')
|
|
||||||
|
|
||||||
|
|
||||||
def _format_toml_value(value: Any) -> str:
|
|
||||||
if isinstance(value, bool):
|
|
||||||
return "true" if value else "false"
|
|
||||||
if isinstance(value, int):
|
|
||||||
return str(value)
|
|
||||||
if isinstance(value, float):
|
|
||||||
return repr(value)
|
|
||||||
if isinstance(value, Path):
|
|
||||||
return f'"{_toml_escape(str(value))}"'
|
|
||||||
if isinstance(value, str):
|
|
||||||
return f'"{_toml_escape(value)}"'
|
|
||||||
if isinstance(value, (list, tuple)):
|
|
||||||
inner = ", ".join(_format_toml_value(item) for item in value)
|
|
||||||
return f"[{inner}]"
|
|
||||||
raise ConfigError(f"Unsupported config value {value!r}")
|
|
||||||
|
|
||||||
|
|
||||||
def _table_has_scalars(table: dict[str, Any]) -> bool:
|
|
||||||
return any(not isinstance(value, dict) for value in table.values())
|
|
||||||
|
|
||||||
|
|
||||||
def dump_toml(config: dict[str, Any]) -> str:
|
def dump_toml(config: dict[str, Any]) -> str:
|
||||||
lines: list[str] = []
|
try:
|
||||||
|
dumped = tomli_w.dumps(config)
|
||||||
def write_kv(key: str, value: Any) -> None:
|
except (TypeError, ValueError) as e:
|
||||||
lines.append(f"{key} = {_format_toml_value(value)}")
|
raise ConfigError(f"Unsupported config value: {e}") from None
|
||||||
|
return dumped
|
||||||
def write_table(name: str, table: dict[str, Any]) -> None:
|
|
||||||
if lines and lines[-1] != "":
|
|
||||||
lines.append("")
|
|
||||||
lines.append(f"[{name}]")
|
|
||||||
for key, value in table.items():
|
|
||||||
if isinstance(value, dict):
|
|
||||||
continue
|
|
||||||
write_kv(key, value)
|
|
||||||
for key, value in table.items():
|
|
||||||
if isinstance(value, dict):
|
|
||||||
write_table(f"{name}.{key}", value)
|
|
||||||
|
|
||||||
for key, value in config.items():
|
|
||||||
if isinstance(value, dict):
|
|
||||||
continue
|
|
||||||
write_kv(key, value)
|
|
||||||
|
|
||||||
for key, value in config.items():
|
|
||||||
if not isinstance(value, dict):
|
|
||||||
continue
|
|
||||||
if _table_has_scalars(value):
|
|
||||||
write_table(key, value)
|
|
||||||
continue
|
|
||||||
for subkey, subvalue in value.items():
|
|
||||||
if isinstance(subvalue, dict):
|
|
||||||
write_table(f"{key}.{subkey}", subvalue)
|
|
||||||
else:
|
|
||||||
write_table(key, value)
|
|
||||||
break
|
|
||||||
|
|
||||||
return "\n".join(lines) + "\n"
|
|
||||||
|
|
||||||
|
|
||||||
def write_config(config: dict[str, Any], path: Path) -> None:
|
def write_config(config: dict[str, Any], path: Path) -> None:
|
||||||
|
|||||||
@@ -581,6 +581,7 @@ dependencies = [
|
|||||||
{ name = "rich" },
|
{ name = "rich" },
|
||||||
{ name = "structlog" },
|
{ name = "structlog" },
|
||||||
{ name = "sulguk" },
|
{ name = "sulguk" },
|
||||||
|
{ name = "tomli-w" },
|
||||||
{ name = "typer" },
|
{ name = "typer" },
|
||||||
{ name = "watchfiles" },
|
{ name = "watchfiles" },
|
||||||
]
|
]
|
||||||
@@ -607,6 +608,7 @@ requires-dist = [
|
|||||||
{ name = "rich", specifier = ">=14.2.0" },
|
{ name = "rich", specifier = ">=14.2.0" },
|
||||||
{ name = "structlog", specifier = ">=25.5.0" },
|
{ name = "structlog", specifier = ">=25.5.0" },
|
||||||
{ name = "sulguk", specifier = ">=0.11.1" },
|
{ name = "sulguk", specifier = ">=0.11.1" },
|
||||||
|
{ name = "tomli-w", specifier = ">=1.2.0" },
|
||||||
{ name = "typer", specifier = ">=0.21.0" },
|
{ name = "typer", specifier = ">=0.21.0" },
|
||||||
{ name = "watchfiles", specifier = ">=0.21.0" },
|
{ name = "watchfiles", specifier = ">=0.21.0" },
|
||||||
]
|
]
|
||||||
@@ -620,6 +622,15 @@ dev = [
|
|||||||
{ name = "ty", specifier = ">=0.0.8" },
|
{ name = "ty", specifier = ">=0.0.8" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tomli-w"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tqdm"
|
name = "tqdm"
|
||||||
version = "4.67.1"
|
version = "4.67.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user