Learning bite
Files and configuration
Read a small configuration file and reject invalid values before acting.
On this page
A configuration file is input, not trusted code
Configuration lets the same program operate on different values without editing its source. A file can be readable yet contain invalid syntax; valid JSON can still have missing fields or impossible values. Check those layers separately so a failure tells the caller what to fix.
In the existing capacity-tool directory, save capacity.json beside capacity.py:
{"total": 20, "used": 8}
JSON objects become Python dictionaries, arrays become lists, and JSON null becomes None. Save read_capacity.py:
import json
from pathlib import Path
from capacity import remaining_gib
def read_capacity(path: Path) -> int:
data = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(data, dict) or set(data) != {"total", "used"}:
raise ValueError("expected exactly total and used fields")
return remaining_gib(data["total"], data["used"])
if __name__ == "__main__":
print(read_capacity(Path("capacity.json")))
Run python read_capacity.py from the project root. Expected output: 12. Path represents a filesystem path. read_text reads bytes and decodes UTF-8; json.loads turns JSON text into Python values. Checking the exact key set catches both missing fields and typos such as uesd. The imported function performs the numeric checks.
Diagnose one layer at a time
Save a copy of the valid fixture before these trials. Restore it between trials.
| Change | Expected failure | What it tells you |
|---|---|---|
Rename capacity.json | FileNotFoundError | The path does not identify a readable file here. |
| Remove a closing brace | JSONDecodeError | The contents are not valid JSON. |
Add "extra": 1 | ValueError from our schema check | Valid JSON does not match this tool's fields. |
Change used to 25 | ValueError from remaining_gib | The values violate our capacity rule. |
Change used to "8" | TypeError | The tool requires a number, not numeric text. |
The uncaught traceback is useful while learning. The next lesson converts these expected failures into a concise CLI error and a nonzero exit status.
Decide how files are located and values are chosen
Relative paths start from the process's current working directory. Running /somewhere/capacity-tool/read_capacity.py from another folder does not move the process there. An explicit configuration argument makes that choice visible.
Python 3.11 includes tomllib for reading TOML. YAML needs an additional library; use its safe data-loading interface, not an arbitrary object loader. Do not use eval to read configuration. The format changes the parser, but the need to validate types and meaning remains.
If a tool has defaults, a file, environment values, and CLI flags, document a winning order. For example, with default timeout 3, file 5, environment 8, and CLI 10, a defaults→file→environment→CLI rule produces 10. Convert and validate the winning value; environment values arrive as strings. Avoid silently treating an invalid override as a reason to use a default.
Writing deserves its own decision. Directly overwriting a live file can leave partial contents if interrupted. A common approach writes a temporary file in the destination directory, validates it, then replaces the destination. Permissions, concurrent writers, and durability still matter; the reporting tool here does not need to write configuration at all.
Checkpoint
Why not accept unknown fields automatically? A misspelling could quietly change behavior. A more extensible format can allow extras deliberately, but this small format promises exactly two fields. Can a valid parser prove used <= total? No; that is a rule of our program.
Restore the valid fixture and retain both Python modules. Next, expose their behavior through arguments, stdout, stderr, and exit codes.
References: pathlib↗, JSON↗, TOML↗, and os.replace↗.
Your notes and evidence
Record observations, questions, or links to your work. Keep credentials out of your notes.
Back up or restore this path
Progress and notes stay in this browser. A backup contains only this learning path.