SPB Git

spb/findata

Public
3.2 KB · 83 lines python
1from __future__ import annotations23from functools import lru_cache4from pathlib import Path56from pydantic import Field, SecretStr7from pydantic_settings import BaseSettings, SettingsConfigDict8910class Settings(BaseSettings):11    """Runtime settings. Secrets are `SecretStr` so that `repr()`/logging never prints them."""1213    model_config = SettingsConfigDict(env_prefix="FINDATA_", env_file=".env", env_file_encoding="utf-8", extra="ignore")1415    env: str = "development"16    root: Path = Field(default_factory=lambda: Path.cwd())17    lake: Path = Field(default_factory=lambda: Path.cwd() / "data" / "lake")18    database_url: SecretStr = SecretStr("postgresql://findata:findata-local@127.0.0.1:5432/findata")19    bind_host: str = "127.0.0.1"20    bind_port: int = 860021    public_url: str = "http://127.0.0.1:8600"22    secret_key: SecretStr = SecretStr("dev-only-change-me-dev-only-change-me")23    lake_soft_limit_gb: float = 250.024    lake_hard_min_free_gb: float = 60.025    # second lake root on the external SSD (cold datasets + raw archives); must contain .findata-cold-root26    lake_cold: Path | None = None27    lake_cold_hard_min_free_gb: float = 100.028    hot_datasets: str = ("market_bars.stock.1m.raw,market_bars.etf.1m.raw,market_bars.stock.1d.raw,market_bars.etf.1d.raw,"29                         "market_bars.stock.5m.raw,market_bars.etf.5m.raw")30    bulk_workers: int = 431    backup_target: str = ""32    # vendor + optional providers (no FINDATA_ prefix: read explicitly)33    frd_userid: SecretStr = Field(default=SecretStr(""), validation_alias="FRD_USERID")34    sec_edgar_user_agent: str = Field(default="", validation_alias="SEC_EDGAR_USER_AGENT")35    anthropic_api_key: SecretStr = Field(default=SecretStr(""), validation_alias="ANTHROPIC_API_KEY")36    openai_api_key: SecretStr = Field(default=SecretStr(""), validation_alias="OPENAI_API_KEY")37    # web static bundle38    web_dist: Path | None = None39    # rate limiting defaults (per API key / session)40    rate_limit_per_minute: int = 60041    query_timeout_seconds: int = 3042    query_max_rows: int = 100_00043    bars_max_rows_json: int = 50_00044    export_ttl_hours: int = 724546    @property47    def is_production(self) -> bool:48        return self.env == "production"4950    @property51    def hot_dataset_ids(self) -> set[str]:52        return {x.strip() for x in self.hot_datasets.split(",") if x.strip()}5354    @property55    def raw_dir(self) -> Path:56        return (self.lake_cold or self.lake) / "raw"5758    @property59    def curated_dir(self) -> Path:60        return self.lake / "curated"6162    @property63    def exports_dir(self) -> Path:64        return self.lake / "exports"6566    @property67    def tmp_dir(self) -> Path:68        return self.lake / "_tmp"6970    def secret_values(self) -> list[str]:71        """Every secret string that must never appear in logs or error messages."""72        vals = [self.frd_userid.get_secret_value(), self.anthropic_api_key.get_secret_value(),73                self.openai_api_key.get_secret_value()]74        pw = self.database_url.get_secret_value()75        if "@" in pw and ":" in pw.split("@")[0]:76            vals.append(pw.split("@")[0].rsplit(":", 1)[-1])77        return [v for v in vals if v and len(v) >= 6]787980@lru_cache81def get_settings() -> Settings:82    return Settings()83
Files · main
Menu

Theme