Typorithm · medium

Settings have a source, a precedence and a validation policy

pythonpydanticindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Settings have a source, a precedence and a validation policy

0%

Concept and contract

Load only a dedicated synthetic environment prefix and disable incidental dotenv files.

Configuration is another external data boundary. Environment variables normally arrive as text, defaults may be used when values are absent, and explicitly supplied initialization values can have higher precedence. A setting that happens to exist on one computer is not a reproducible application configuration contract.

The lab uses BaseSettings from the separate pydantic_settings package and a dedicated LESSON_API_ prefix. It reads a small mode and page-size policy. Dotenv and secret-directory loading are explicitly disabled in this owned fixture so running a lesson does not accidentally import a real project configuration file or a credential store.

Mechanism and worked example

The inspection helper temporarily sets only the two teaching environment keys and restores their previous values in a finally block.

The inspection helper temporarily sets only the two teaching environment keys and restores their previous values in a finally block. It then creates Settings with the explicitly declared sources and optional initialization values. An environment page size of "5" becomes integer 5, and an explicit initialization page size can override the environment value.

A mode outside the declared Literal vocabulary fails validation. The helper does not clear the complete process environment or print it. Those details matter even in a small fixture: isolation should protect unrelated settings rather than merely hoping that a developer computer contains no sensitive variables.

Complete worked example

python

import os
from typing import Literal
from pydantic import Field, ValidationError
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="LESSON_API_", env_file=None)
    mode: Literal["local", "test"] = "local"
    page_size: int = Field(default=10, ge=1, le=20)

def inspect_example(request):
    keys = ["LESSON_API_MODE", "LESSON_API_PAGE_SIZE"]
    before = {key: os.environ.get(key) for key in keys}
    try:
        for key in keys:
            os.environ.pop(key, None)
        for key, value in request.get("env", {}).items():
            assert key in ("MODE", "PAGE_SIZE")
            os.environ["LESSON_API_" + key] = value
        try:
            settings = Settings(_env_file=None, _secrets_dir=None,
                                **request.get("init", {}))
            return {"value": settings.model_dump()}
        except ValidationError as error:
            return {"error": sorted({e["type"] for e in error.errors()})}
    finally:
        for key, value in before.items():
            if value is None:
                os.environ.pop(key, None)
            else:
                os.environ[key] = value
Reasoning
The inspection helper temporarily sets only the two teaching environment keys and restores their previous values in a finally block. It then creates Settings with the explicitly declared sources and optional initialization values. An environment page size of "5" becomes integer 5, and an explicit initialization page size can override the environment value. A mode outside the declared Literal vocabulary fails validation. The helper does not clear the complete process environment or print it. Those details matter even in a small fixture: isolation should protect unrelated settings rather than merely hoping that a developer computer contains no sensitive variables.

Focused decision: use the complete example for context

python

from typing import Literal
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="LESSON_API_", env_file=None)
    mode: Literal["local", "test"] = "local"
    page_size: int = Field(default=10, ge=1, le=20)
Focused decision
Reconstruct this focused decision after the warm-up. The complete imports, model or application, and test client remain in the worked example. Typing records practice; only the downloadable computer lab executes Python.

Counterexamples and common mistakes

Removing the prefix can cause the intended teaching variables to be ignored and can make a process read similarly named ambient values instead.

Removing the prefix can cause the intended teaching variables to be ignored and can make a process read similarly named ambient values instead. The counterexample changes the prefix, so the expected environment-driven page size is not obtained. Defaults can hide this mistake by allowing startup to appear successful.

Do not log an entire settings object when it contains secrets, and do not treat a checked setting as permission to contact an arbitrary endpoint. Source precedence, value validation, secret handling and destination policy are different concerns. Deployment code must decide how the accepted configuration is provided and refreshed.

Controlled mistake: observe the actual wrong behavior

python

import os
from typing import Literal
from pydantic import Field, ValidationError
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="UNRELATED_LESSON_", env_file=None)
    mode: Literal["local", "test"] = "local"
    page_size: int = Field(default=10, ge=1, le=20)

def inspect_example(request):
    keys = ["LESSON_API_MODE", "LESSON_API_PAGE_SIZE"]
    before = {key: os.environ.get(key) for key in keys}
    try:
        for key in keys:
            os.environ.pop(key, None)
        for key, value in request.get("env", {}).items():
            assert key in ("MODE", "PAGE_SIZE")
            os.environ["LESSON_API_" + key] = value
        try:
            settings = Settings(_env_file=None, _secrets_dir=None,
                                **request.get("init", {}))
            return {"value": settings.model_dump()}
        except ValidationError as error:
            return {"error": sorted({e["type"] for e in error.errors()})}
    finally:
        for key, value in before.items():
            if value is None:
                os.environ.pop(key, None)
            else:
                os.environ[key] = value
Reasoning
Removing the prefix can cause the intended teaching variables to be ignored and can make a process read similarly named ambient values instead. The counterexample changes the prefix, so the expected environment-driven page size is not obtained. Defaults can hide this mistake by allowing startup to appear successful. Do not log an entire settings object when it contains secrets, and do not treat a checked setting as permission to contact an arbitrary endpoint. Source precedence, value validation, secret handling and destination policy are different concerns. Deployment code must decide how the accepted configuration is provided and refreshed.

Independent practice: one bounded change

Increase the maximum accepted page size from twenty to fifty.

Increase the maximum accepted page size from twenty to fifty. Preserve the positive lower bound, the dedicated prefix, source isolation and initialization precedence. A configured value of twenty-five should now pass; fifty-one and zero must still fail their respective bounds.

Change only the Field bound and run the supplied environment cases. Do not make every field optional or ignore a ValidationError to keep startup running with an invalid value. A small upper-bound change should be visible in the accepted configuration and should not alter where the value came from.

Step 1: Allow a configured page size up to fifty without widening the mode vocabulary or changing source precedence. Step 2: Retain the working imports and application structure. Change the stated policy, run the supplied practice cases, then explain the newly accepted or rejected boundary.

Practice starter: solve before reading the solution

python

import os
from typing import Literal
from pydantic import Field, ValidationError
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="LESSON_API_", env_file=None)
    mode: Literal["local", "test"] = "local"
    page_size: int = Field(default=10, ge=1, le=20)

def inspect_example(request):
    keys = ["LESSON_API_MODE", "LESSON_API_PAGE_SIZE"]
    before = {key: os.environ.get(key) for key in keys}
    try:
        for key in keys:
            os.environ.pop(key, None)
        for key, value in request.get("env", {}).items():
            assert key in ("MODE", "PAGE_SIZE")
            os.environ["LESSON_API_" + key] = value
        try:
            settings = Settings(_env_file=None, _secrets_dir=None,
                                **request.get("init", {}))
            return {"value": settings.model_dump()}
        except ValidationError as error:
            return {"error": sorted({e["type"] for e in error.errors()})}
    finally:
        for key, value in before.items():
            if value is None:
                os.environ.pop(key, None)
            else:
                os.environ[key] = value

# Independent task: Allow a configured page size up to fifty without widening the mode vocabulary or changing source precedence.
Reasoning
Allow a configured page size up to fifty without widening the mode vocabulary or changing source precedence.
  • Hint 1: The change belongs to page_size, not to the environment helper.
  • Hint 2: Change le=20 to le=50 and retain ge=1.

Solution, tests and trade-offs

The solution uses le=50 while keeping ge=1.

The solution uses le=50 while keeping ge=1. The fixture verifies an accepted environment value of twenty-five, an excessive value and a nonpositive value. The same Settings type still controls defaults and parses environment strings; the helper still restores its temporary teaching keys.

A production service may choose command-line sources, secret providers, reload behavior or a different precedence. State and test that selection rather than assuming the lesson covers every settings deployment. The dependency lock records the versions actually exercised on the computer; it is not installed on the ApplyDjinn production servers.

Practice solution and acceptance checks

python

import os
from typing import Literal
from pydantic import Field, ValidationError
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="LESSON_API_", env_file=None)
    mode: Literal["local", "test"] = "local"
    page_size: int = Field(default=10, ge=1, le=50)

def inspect_example(request):
    keys = ["LESSON_API_MODE", "LESSON_API_PAGE_SIZE"]
    before = {key: os.environ.get(key) for key in keys}
    try:
        for key in keys:
            os.environ.pop(key, None)
        for key, value in request.get("env", {}).items():
            assert key in ("MODE", "PAGE_SIZE")
            os.environ["LESSON_API_" + key] = value
        try:
            settings = Settings(_env_file=None, _secrets_dir=None,
                                **request.get("init", {}))
            return {"value": settings.model_dump()}
        except ValidationError as error:
            return {"error": sorted({e["type"] for e in error.errors()})}
    finally:
        for key, value in before.items():
            if value is None:
                os.environ.pop(key, None)
            else:
                os.environ[key] = value
Reasoning
The solution uses le=50 while keeping ge=1. The fixture verifies an accepted environment value of twenty-five, an excessive value and a nonpositive value. The same Settings type still controls defaults and parses environment strings; the helper still restores its temporary teaching keys. A production service may choose command-line sources, secret providers, reload behavior or a different precedence. State and test that selection rather than assuming the lesson covers every settings deployment. The dependency lock records the versions actually exercised on the computer; it is not installed on the ApplyDjinn production servers.

Interview reasoning and next step

In an interview, explain a concrete precedence example and show a failure that a permissive default could otherwise hide.

In an interview, explain a concrete precedence example and show a failure that a permissive default could otherwise hide. Identify which environment keys the program reads and which file or secret sources it intentionally does not use. This is more reliable than presenting configuration as an unstructured collection of strings.

The final modelling capstone combines validation, scope, duplicate identity and computed output. Carry the same discipline into it: label the data source, define the accepted shape and make every derived number explain what it counts. A valid configuration cannot compensate for an unclear report contract.

Optional stretch — after the core task works: Add one independently reasoned boundary case and explain its expected value before running it.

Computer practice: https://applydjinn.com/typorithm/python-api-lab-20261105.zip. Use an isolated Python environment and the pinned dependencies. Run verify.py --track pydantic, then edit the working exercise baseline and run --practice pyd-settings. The reference suite rejects meaningful counterexamples and checks values, types and unchanged inputs. All data is owned and all HTTP clients are in-process. No production database, feed, account or paid service is used.

Load only a dedicated synthetic environment prefix and disable incidental dotenv files.