2026-08-02 17:44:57 -07:00
|
|
|
"""Typed reading of the container's environment.
|
|
|
|
|
|
|
|
|
|
Everything in the environment is text, including values the client later
|
|
|
|
|
compares as numbers or passes to seaf-cli. Reading them directly leaves two
|
|
|
|
|
failures that are silent until much later: an unset variable becomes None and
|
|
|
|
|
reaches seaf-cli as the string "None", and a numeric setting stays a string
|
|
|
|
|
that never equals the integer it is compared against.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import os
|
2026-08-02 18:08:38 -07:00
|
|
|
from typing import Optional
|
2026-08-02 17:44:57 -07:00
|
|
|
|
|
|
|
|
from dsc.errors import ConfigError
|
|
|
|
|
|
|
|
|
|
_TRUE_VALUES = {"true", "1", "yes", "on"}
|
|
|
|
|
_FALSE_VALUES = {"false", "0", "no", "off"}
|
|
|
|
|
|
|
|
|
|
|
2026-08-02 18:08:38 -07:00
|
|
|
def env_str(name: str, default: Optional[str] = None) -> Optional[str]:
|
2026-08-02 17:44:57 -07:00
|
|
|
"""Return a text setting. An empty variable counts as unset."""
|
|
|
|
|
value = os.getenv(name)
|
|
|
|
|
if value is None or value == "":
|
|
|
|
|
return default
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def env_int(name: str, default: int, minimum: int = 0) -> int:
|
|
|
|
|
"""
|
|
|
|
|
Return a numeric setting as an int, never as text and never as None.
|
|
|
|
|
|
|
|
|
|
Values below ``minimum`` are rejected: a negative speed limit or deletion
|
|
|
|
|
threshold is not something to pass on to seaf-cli and hope for the best.
|
|
|
|
|
"""
|
|
|
|
|
raw = env_str(name)
|
|
|
|
|
if raw is None:
|
|
|
|
|
return default
|
|
|
|
|
|
|
|
|
|
stripped = raw.strip()
|
|
|
|
|
try:
|
|
|
|
|
value = int(stripped, 10)
|
|
|
|
|
except ValueError:
|
|
|
|
|
raise ConfigError(
|
|
|
|
|
f"{name} must be a whole number, got {stripped!r}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if value < minimum:
|
|
|
|
|
raise ConfigError(
|
|
|
|
|
f"{name} must be {minimum} or greater, got {stripped!r}"
|
|
|
|
|
)
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def env_bool(name: str, default: bool = False) -> bool:
|
|
|
|
|
"""Return a boolean setting, accepting the usual spellings."""
|
|
|
|
|
raw = env_str(name)
|
|
|
|
|
if raw is None:
|
|
|
|
|
return default
|
|
|
|
|
|
|
|
|
|
value = raw.strip().lower()
|
|
|
|
|
if value in _TRUE_VALUES:
|
|
|
|
|
return True
|
|
|
|
|
if value in _FALSE_VALUES:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
accepted = ", ".join(sorted(_TRUE_VALUES | _FALSE_VALUES))
|
|
|
|
|
raise ConfigError(f"{name} must be one of {accepted}, got {raw.strip()!r}")
|