Files
zeekay e75e7bad0d fix(python): tomllib backport for Python 3.10
config.py imported the stdlib 'tomllib' unconditionally, but that module
only exists in 3.11+. On 3.10 the import crashed at package-import time,
breaking ALL test collection (pytest exit 2) despite requires-python>=3.10.

Use tomli as 'tomllib' on <3.11 (identical API; tomllib is tomli upstream)
and declare 'tomli>=2.0; python_version < 3.11' as a runtime dependency.

Green now on 3.10 / 3.11 / 3.12: ruff + mypy clean, 129 passed, ~91% cov.
2026-06-20 02:30:51 -07:00

94 lines
2.2 KiB
Python

"""ZAP configuration."""
from __future__ import annotations
import sys
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import Any
if sys.version_info >= (3, 11):
import tomllib
else: # Python 3.10: tomllib was added to the stdlib in 3.11
import tomli as tomllib
class Transport(Enum):
"""Transport type."""
STDIO = "stdio"
HTTP = "http"
WEBSOCKET = "websocket"
ZAP = "zap"
UNIX = "unix"
@dataclass
class Auth:
"""Authentication config."""
type: str = "none"
token: str | None = None
username: str | None = None
password: str | None = None
@dataclass
class ServerConfig:
"""Server configuration."""
name: str
url: str
transport: Transport = Transport.STDIO
timeout: int = 30000
auth: Auth | None = None
@dataclass
class Config:
"""ZAP configuration."""
listen: str = "0.0.0.0"
port: int = 9999
servers: list[ServerConfig] = field(default_factory=list)
log_level: str = "info"
@classmethod
def load(cls, path: Path) -> Config:
"""Load config from file."""
with open(path, "rb") as f:
data = tomllib.load(f)
return cls.from_dict(data)
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Config:
"""Create config from dict."""
servers = [
ServerConfig(
name=s["name"],
url=s["url"],
transport=Transport(s.get("transport", "stdio")),
timeout=s.get("timeout", 30000),
)
for s in data.get("servers", [])
]
return cls(
listen=data.get("listen", "0.0.0.0"),
port=data.get("port", 9999),
servers=servers,
log_level=data.get("log_level", "info"),
)
@staticmethod
def default_path() -> Path:
"""Get default config path."""
import platform
if platform.system() == "Darwin":
return Path.home() / "Library" / "Application Support" / "zap" / "config.toml"
elif platform.system() == "Windows":
return Path.home() / "AppData" / "Roaming" / "zap" / "config.toml"
else:
return Path.home() / ".config" / "zap" / "config.toml"