โ† Semua pembelajaran / Python untuk AI Engineer
Fase 2 ยท Type Hints & Pydantic

Pydantic Settings

Menggantikan os.getenv() yang tersebar dengan satu objek konfigurasi yang divalidasi sekali saat aplikasi start.

Sumber asli pydantic.dev Resmi Rangkuman ~5 menit baca

Intisari

  • Satu class Settings menggantikan semua panggilan os.getenv() di seluruh kodebase.
  • Field tanpa nilai default berarti wajib โ€” aplikasi gagal start kalau env var-nya kosong.
  • Baca otomatis dari environment dan file .env, dengan konversi tipe ("8000" โ†’ 8000).
  • Pakai SecretStr untuk API key supaya tidak bocor ke log.
  • Gagal saat start jauh lebih baik daripada gagal di request pertama pengguna.

Masalah yang diselesaikan

# Tersebar di berbagai file
api_key = os.getenv("ANTHROPIC_API_KEY")             # None kalau lupa diatur
max_tokens = int(os.getenv("MAX_TOKENS", "4096"))    # crash kalau isinya bukan angka
debug = os.getenv("DEBUG") == "true"                 # "True" dan "1" tidak terdeteksi

Tiga masalah sekaligus: tidak tervalidasi, konversi tipe manual, dan kegagalan baru muncul saat runtime.

uv add pydantic-settings
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        extra="ignore",
    )

    # Wajib โ€” tidak ada default, jadi aplikasi gagal start kalau kosong
    anthropic_api_key: SecretStr

    # Punya default
    model: str = "claude-opus-5"
    max_tokens: int = Field(default=4096, ge=1, le=128_000)
    aws_region: str = "us-east-1"
    debug: bool = False
    allowed_origins: list[str] = ["http://localhost:3000"]


settings = Settings()     # dibaca & divalidasi SEKALI, saat modul diimpor

Poin utamanya: kalau ANTHROPIC_API_KEY tidak diatur, aplikasi gagal start dengan pesan yang jelas โ€” bukan berjalan normal lalu error 500 di request pertama pengguna. Ini perbedaan antara masalah yang ketahuan saat deploy dan masalah yang ketahuan dari laporan pengguna.

Dari mana nilainya diambil

Urutan prioritas, dari yang paling menang:

  1. Argumen saat membuat objek: Settings(debug=True)
  2. Environment variable
  3. File .env
  4. File secret (/run/secrets/... โ€” untuk Docker)
  5. Nilai default di class

Nama field dipetakan ke env var secara case-insensitive:

anthropic_api_key  โ†  ANTHROPIC_API_KEY
max_tokens         โ†  MAX_TOKENS
aws_region         โ†  AWS_REGION

Contoh .env

ANTHROPIC_API_KEY=sk-ant-xxxxx
MAX_TOKENS=8192
DEBUG=true
ALLOWED_ORIGINS=["https://app.contoh.com","https://admin.contoh.com"]

Masukkan .env ke .gitignore. Commit .env.example yang berisi nama-nama variabel dengan nilai kosong atau contoh โ€” supaya orang lain tahu apa yang perlu diisi tanpa kamu membocorkan kredensial.

Prefix

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="APP_")

    nama: str          # โ† dibaca dari APP_NAMA
    port: int = 8000   # โ† dibaca dari APP_PORT

Berguna supaya konfigurasi aplikasimu tidak bertabrakan dengan env var sistem.

Konfigurasi bersarang

from pydantic import BaseModel

class DatabaseSettings(BaseModel):
    host: str = "localhost"
    port: int = 5432

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_nested_delimiter="__")

    db: DatabaseSettings = DatabaseSettings()
DB__HOST=prod.db.internal
DB__PORT=5433

SecretStr

settings.anthropic_api_key                     # SecretStr('**********')
print(settings)                                 # key TIDAK muncul
logger.info("config: %s", settings)             # aman

settings.anthropic_api_key.get_secret_value()   # ambil nilai asli, eksplisit

Nilai SecretStr adalah pertahanan berlapis. Kebocoran API key ke log paling sering terjadi karena seseorang mencetak seluruh objek konfigurasi saat debugging. Dengan SecretStr, itu tidak mungkin terjadi tanpa sengaja.

Pola pemakaian

Objek modul (paling sederhana)

# src/myapp/settings.py
settings = Settings()

# di file lain
from myapp.settings import settings
client = anthropic.Anthropic(api_key=settings.anthropic_api_key.get_secret_value())

Fungsi ter-cache (lebih ramah testing)

from functools import lru_cache

@lru_cache
def get_settings() -> Settings:
    return Settings()

Dengan bentuk ini, test bisa memanggil get_settings.cache_clear() lalu menyuntikkan konfigurasi berbeda.

Bersama FastAPI (Fase 5)

from typing import Annotated
from fastapi import Depends

@app.get("/config")
def baca_config(s: Annotated[Settings, Depends(get_settings)]):
    return {"model": s.model, "max_tokens": s.max_tokens}

Validasi tambahan

from typing import Literal, Self
from pydantic import model_validator

class Settings(BaseSettings):
    lingkungan: Literal["dev", "staging", "prod"] = "dev"
    debug: bool = False

    @model_validator(mode="after")
    def debug_dilarang_di_prod(self) -> Self:
        if self.lingkungan == "prod" and self.debug:
            raise ValueError("debug tidak boleh aktif di production")
        return self

Di dalam Docker

services:
  api:
    image: myapp
    environment:
      - MAX_TOKENS=8192
      - AWS_REGION=ap-southeast-3
    secrets:
      - anthropic_api_key

secrets:
  anthropic_api_key:
    file: ./secrets/anthropic_api_key
class Settings(BaseSettings):
    model_config = SettingsConfigDict(secrets_dir="/run/secrets")
    anthropic_api_key: SecretStr

Pydantic Settings membaca file di secrets_dir yang namanya cocok dengan nama field. Ini cara yang benar untuk menangani rahasia di container โ€” nilainya tidak pernah muncul di docker inspect atau daftar environment variable.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.