Typorithm · easy

Configuration as an explicit input

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

Step 1 of 4 · intro card

Configuration as an explicit input

0%

Concept and contract

Turn environment-style strings into a small validated configuration without reading ambient process state.

A reusable client should receive its configuration explicitly instead of reading environment variables inside every helper. Operating-system environment values are strings, so a value such as LIMIT=2 is not yet an integer.

This lesson accepts a dictionary representing that boundary. Defaults apply only when a key is absent; a present empty or malformed value is evidence of a configuration problem, not permission to silently choose another setting.

Mechanism and worked example

The worked function obtains two values, validates their original types, normalizes the country and converts the limit only after checking its ASCII digits and range.

The worked function obtains two values, validates their original types, normalizes the country and converts the limit only after checking its ASCII digits and range. Passing an explicit mapping makes repeated calls independent of your terminal session.

The accepted country set belongs to this fictional client, not a universal list of markets. No environment mutation, file access or network request happens while validating configuration.

Complete worked example

python

def settings(values):
    country = values.get("COUNTRY", "US")
    limit = values.get("LIMIT", "2")
    if not isinstance(country, str) or not isinstance(limit, str):
        raise ValueError("settings must be strings")
    country = country.strip().upper()
    if country not in {"US", "IL", "UA", "CA"}:
        raise ValueError("unsupported country")
    if not limit.isascii() or not limit.isdecimal() or not 1 <= int(limit) <= 5:
        raise ValueError("limit must be 1..5")
    return {"country": country, "limit": int(limit)}
Reasoning
Turn environment-style strings into a small validated configuration without reading ambient process state. Compare the stated values and failure boundary before changing the implementation.

Counterexamples and common mistakes

The counterexample uses truthiness to replace a supplied zero-like limit with a default.

The counterexample uses truthiness to replace a supplied zero-like limit with a default. That conceals a meaningful invalid input.

Another common mistake is returning the original mapping after inserting normalized values, which changes caller-owned configuration. Treat missing, empty and invalid values as distinct cases. Avoid dumping the full configuration when reporting an error because a real configuration mapping may also contain credentials.

Counterexample: a runnable policy defect

python

def settings(values):
    country = values.get("COUNTRY", "US")
    limit = values.get("LIMIT") or "2"
    if not isinstance(country, str) or not isinstance(limit, str):
        raise ValueError("settings must be strings")
    country = country.strip().upper()
    if country not in {"US", "IL", "UA", "CA"}:
        raise ValueError("unsupported country")
    if not limit.isascii() or not limit.isdecimal() or not 1 <= int(limit) <= 5:
        raise ValueError("limit must be 1..5")
    return {"country": country, "limit": int(limit)}
Reasoning
This runnable counterexample violates the declared contract; the companion lab requires it to fail.

Independent practice: API client task

Extend the configuration with a TIMEOUT setting expressed as a whole number of seconds from one through ten, defaulting to three.

Extend the configuration with a TIMEOUT setting expressed as a whole number of seconds from one through ten, defaulting to three. Keep the existing country and limit rules intact.

Start by checking the omitted field, then add a supplied valid value and a bad value. This is one new field at the same boundary, rather than an exercise in loading an entire settings framework.

Step 1: Add the TIMEOUT field with a default of three seconds. Step 2: Reject non-string and out-of-range timeout values.

Practice starter: solve before reading the solution

python

def settings(values):
    # Remove this placeholder, then adapt the retained baseline:
    # Add the TIMEOUT field with a default of three seconds.
    raise NotImplementedError
    country = values.get("COUNTRY", "US")
    limit = values.get("LIMIT", "2")
    if not isinstance(country, str) or not isinstance(limit, str):
        raise ValueError("settings must be strings")
    country = country.strip().upper()
    if country not in {"US", "IL", "UA", "CA"}:
        raise ValueError("unsupported country")
    if not limit.isascii() or not limit.isdecimal() or not 1 <= int(limit) <= 5:
        raise ValueError("limit must be 1..5")
    return {"country": country, "limit": int(limit)}
Reasoning
Add the TIMEOUT field with a default of three seconds. Reject non-string and out-of-range timeout values.
  • Hint 1: Reuse the ASCII digit and range checks before int conversion.
  • Hint 2: Return timeout as an integer without editing values.

Solution, tests and trade-offs

The reference solution returns a fresh dictionary and leaves the supplied mapping unchanged.

The reference solution returns a fresh dictionary and leaves the supplied mapping unchanged. Its fixture cases include lower-case country text, omitted keys, an empty string, an out-of-range limit and a boolean pretending to be a configuration value.

The local checker compares exact serialized output, so a string limit cannot accidentally pass as an integer. Add your timeout cases before opening the supplied solution.

Practice solution and acceptance checks

python

def settings(values):
    country = values.get("COUNTRY", "US")
    limit = values.get("LIMIT", "2")
    if not isinstance(country, str) or not isinstance(limit, str):
        raise ValueError("settings must be strings")
    country = country.strip().upper()
    if country not in {"US", "IL", "UA", "CA"}:
        raise ValueError("unsupported country")
    if not limit.isascii() or not limit.isdecimal() or not 1 <= int(limit) <= 5:
        raise ValueError("limit must be 1..5")
    timeout = values.get("TIMEOUT", "3")
    if not isinstance(timeout, str) or not timeout.isascii() or not timeout.isdecimal() or not 1 <= int(timeout) <= 10:
        raise ValueError("timeout must be 1..10")
    return {"country": country, "limit": int(limit), "timeout": int(timeout)}
Reasoning
Reuse the ASCII digit and range checks before int conversion. Return timeout as an integer without editing values.

Interview reasoning and next step

Explain where configuration enters your program and which layer owns defaults.

Explain where configuration enters your program and which layer owns defaults. In a larger command-line tool, main can read os.environ once and pass the selected values to this function.

A web handler may receive a different explicit mapping. Neither approach makes environment variables a secret store by itself. The next lesson adds command-line flags while retaining the same separation between external text and validated application values.

Optional stretch — after the core task works: Allow a separately validated decimal timeout and explain its precision policy.

Computer practice: https://applydjinn.com/typorithm/python-automation-lab-20261105.zip. Run verify.py using your selected Python interpreter, then adapt the retained exercise baseline and check it with --practice followed by the lesson id. The standard-library lab uses owned temporary 127.0.0.1 loopback servers, fictional pages and disposable files; it never scans production feeds or opens the working ApplyDjinn database.

Turn environment-style strings into a small validated configuration without reading ambient process state.