FastAPI — Types Are the Contract
Configuration typos must not become defaults
Goal
You test strict configuration parsing, removal of secrets, and a per-app snapshot.
Why it matters
Converting the string false to a bool gave True. In production, debug responses were switched on, and the status page printed the entire configuration dictionary. Environment variables are strings, so a type declaration alone does not make a value safe. When the configuration is read and how much of it is made public are also part of the application contract.
Steps
- In
/root/work/fa-config-lab/service.py, parse_bool(value) returns a bool only for true or false, ignoring case. If there is surrounding whitespace or the value is not a string, it is ValueError.
Prepare this once at the start. Existing files are not overwritten.
mkdir -p /root/work/fa-config-lab
test -e /root/work/fa-config-lab/service.py || cp /opt/fixtures/ten_labs/fa-config-lab/service.py /root/work/fa-config-lab/service.py
cd /root/work/fa-config-lab
-
In
/root/work/fa-config-lab/service.py, parse_port(value) converts a string that has only ASCII digits to an int and returns it if it is 1–65535. Everything else is ValueError. -
In
/root/work/fa-config-lab/service.py, parse_timeout(value) converts a string to a float and returns only finite values greater than 0 and at most 30. Everything else is ValueError. -
In
/root/work/fa-config-lab/service.py, required_token(env) returns the stripped value when TOKEN is a string and is not empty after strip. A missing or empty value is ValueError. -
In
/root/work/fa-config-lab/service.py, load_settings(env) is a dictionary with service=SERVICE of env or 'api' if missing, debug=parse_bool(DEBUG, or 'false' if missing), port=parse_port(PORT, or '8000' if missing), timeout=parse_timeout(TIMEOUT, or '5' if missing), and token=required_token. An empty SERVICE is ValueError. -
In
/root/work/fa-config-lab/service.py, public_settings(settings) is a new dictionary that has only service and debug. It does not modify the original. -
In
/root/work/fa-config-lab/service.py, snapshot(env) returns the result of load_settings. Even if env is modified after the call, the returned settings do not change. -
In
/root/work/fa-config-lab/service.py, create_app(env) reads the snapshot immediately, and if the settings are invalid it makes app creation fail with ValueError. GET /info returns only public_settings. Apps created with different env values do not share settings.
Notes
- You work in the existing lab-dev environment with no internet and no package installation.
- Each step runs within a 45-second grading budget. Do not add real sleeps or network calls.
- The grader loads the submitted module fresh and checks it with independent inputs and a temporary DB. Implement the contract instead of returning the expected values as constants.
- FastAPI official documentation · pytest official documentation · Python sqlite3
- Limitation: the environment is injected as an ordinary dictionary, so it does not depend on the real process-wide environment. This is not an example that implements an encrypted secret store, key rotation, or dynamic reloading. The token string is teaching input, and you never put a real production key into a lab Pod.
Parse the boolean explicitly
In /root/work/fa-config-lab/service.py, parse_bool(value) returns a bool only for true or false, ignoring case. If there is surrounding whitespace or the value is not a string, it is ValueError.
Prepare this once at the start. Existing files are not overwritten.
mkdir -p /root/work/fa-config-lab
test -e /root/work/fa-config-lab/service.py || cp /opt/fixtures/ten_labs/fa-config-lab/service.py /root/work/fa-config-lab/service.py
cd /root/work/fa-config-lab
bool('false') is True. Compare against the two allowed strings directly.
After saving, check with bash /opt/lab/checks/fa-config-lab/01-contract.sh.
Check the port range
In /root/work/fa-config-lab/service.py, parse_port(value) converts a string that has only ASCII digits to an int and returns it if it is 1–65535. Everything else is ValueError.
A successful integer conversion does not mean the value is in the valid port range.
After saving, check with bash /opt/lab/checks/fa-config-lab/02-contract.sh.
Make the time limit a finite value
In /root/work/fa-config-lab/service.py, parse_timeout(value) converts a string to a float and returns only finite values greater than 0 and at most 30. Everything else is ValueError.
NaN behaves unexpectedly in ordinary comparisons, so check isfinite.
After saving, check with bash /opt/lab/checks/fa-config-lab/03-contract.sh.
Reject a missing required secret
In /root/work/fa-config-lab/service.py, required_token(env) returns the stripped value when TOKEN is a string and is not empty after strip. A missing or empty value is ValueError.
Do not replace a missing required secret with a sample default.
After saving, check with bash /opt/lab/checks/fa-config-lab/04-contract.sh.
Apply defaults only to missing values
In /root/work/fa-config-lab/service.py, load_settings(env) is a dictionary with service=SERVICE of env or 'api' if missing, debug=parse_bool(DEBUG, or 'false' if missing), port=parse_port(PORT, or '8000' if missing), timeout=parse_timeout(TIMEOUT, or '5' if missing), and token=required_token. An empty SERVICE is ValueError.
The default of get and 'value or default' differ in how they treat an empty string.
After saving, check with bash /opt/lab/checks/fa-config-lab/05-contract.sh.
Remove secrets from the public settings
In /root/work/fa-config-lab/service.py, public_settings(settings) is a new dictionary that has only service and debug. It does not modify the original.
Rather than masking part of the token value, use a contract in which the field itself is not exposed.
After saving, check with bash /opt/lab/checks/fa-config-lab/06-contract.sh.
Separate outside changes from the settings
In /root/work/fa-config-lab/service.py, snapshot(env) returns the result of load_settings. Even if env is modified after the call, the returned settings do not change.
Separate the settings at app startup from an input dictionary that may change later.
After saving, check with bash /opt/lab/checks/fa-config-lab/07-contract.sh.
Check startup failure and the public response
In /root/work/fa-config-lab/service.py, create_app(env) reads the snapshot immediately, and if the settings are invalid it makes app creation fail with ValueError. GET /info returns only public_settings. Apps created with different env values do not share settings.
Validate at creation so that a configuration error does not first show up on the first request after the server has started.
After saving, check with bash /opt/lab/checks/fa-config-lab/08-contract.sh.