R01 · Week 1 Recall & Drill
Week 1 revision: reproducible dev environments, Git's object model, shell pipelines, doc-hunting strategy, and Python's reference-based memory model.
Original publication date unrecordedUpdated
Lesson preparation & details
Level: intermediate
By the end, you should be able to
- Specify a reproducible exercise environment and separately diagnose CRLF line endings, filesystem placement and package isolation; no setup script guarantees all three are prevented.
- State what a commit, a branch, and a remote actually are — snapshot, movable branch reference, named remote configuration with URLs and refspecs — and recover lost work with git reflog.
- Write a shell pipeline that answers a real question from a raw log file, and explain that each stage is a separate process reading stdin and writing stdout.
- Pick the right doc type for the question you have (tutorial, how-to, reference, explanation) and fall through the doc-hunting funnel when the first source fails.
- Draw the memory diagram for any Python assignment and predict whether a mutation is visible through a second name.
- Spot the mutable-default-argument bug on sight and give two fixes.
Bring with you
Sources checked: . A source check does not change the publication or update date.
Editorial review: · What review means
In this article · 21 sections
Assessment scope
Attempt the recall prompts before reading the worked drill and answer explanations. The worked examples below restore the combined teaching task, with explicit contracts and checks; a local model is distinguished from a reproduced external service. The minute allocations are adjustable practice suggestions, not evidence of learning speed.
Sessions covered
- S001 — Dev Environment — Linux/WSL, Terminal, VS Code
- S002 — Git & GitHub — Commits, Branches, PRs
- S003 — The Command Line — bash, pipes, grep, jq
- S004 — Reading Docs & Effective Googling — the Meta-Skill
- S005 — Python Variables & Types — Mental Model of Memory
By the end of this revision you can
-
Specify a reproducible exercise environment and separately diagnose CRLF line endings, filesystem placement and package isolation; no setup script guarantees all three are prevented.
-
State what a commit, a branch, and a remote actually are — snapshot, movable branch reference, named remote configuration with URLs and refspecs — and recover lost work with git reflog.
-
Write a shell pipeline that answers a real question from a raw log file, and explain that each stage is a separate process reading stdin and writing stdout.
-
Pick the right doc type for the question you have (tutorial, how-to, reference, explanation) and fall through the doc-hunting funnel when the first source fails.
-
Draw the memory diagram for any Python assignment and predict whether a mutation is visible through a second name.
-
Spot the mutable-default-argument bug on sight and give two fixes.
90-min structure
Treat this as an adjustable practice budget: 5 minutes to recall the five subjects, 30 for the blank-page questions, 30 for the worked drill, 15 to compare answers and test a counterexample, and 10 to record a gap and plan an unseen retest. The solutions and transfer exercises can take longer; the goal is correct reasoning, not racing a timer.
Blank-page reconstruction · 30 min
No tabs open. Paper or a scratch file only. Three prompts per session, then check the gotcha.
S001 · Dev Environment
- Write the skeleton of
setup.shfrom memory — what does it install, in what order, and what makes it safe to re-run twice in a row? - Explain the difference between WSL 1 and WSL 2 in one sentence, and identify the compatibility requirements that determine your choice.
- Name the three problems that environment tooling exists to solve, and give one tool that solves each.
S002 · Git & GitHub
- Draw the three areas a file moves between — working tree, staging area, repository — and label the command that moves a change across each boundary.
- Define a commit, a branch, and a remote in one sentence each, with no analogy that breaks under pressure.
- You committed three times on the wrong branch. Write the exact command sequence that preserves those commits on a new branch; explain why removing them from the old branch is a separate decision.
S003 · The Command Line
- Draw what happens when you run
cat access.log | grep 500 | wc -l. How many processes exist? Which file descriptors are wired to which? - Write out
set -euo pipefailand explain the three options and their important exceptions, one line each. - Answer this with a pipeline: which endpoint appeared most often in a log file? Assume the path is the seventh whitespace-separated field.
S004 · Reading Docs & Effective Googling
- Name the four Diátaxis documentation types and say which one you reach for when a tool is already installed and you have a specific task.
- List the sections of a
manpage in order and say which one you should read first. - Write a search query that returns only official documentation for a specific flag of a specific tool — no blog spam.
S005 · Python Variables & Types
- Draw the memory diagram for
x = [1, 2, 3], theny = x, theny.append(4). What doesxprint? - Sort these into mutable and immutable:
int,str,tuple,list,dict,set,frozenset,bytes. - Explain when a tuple can be a dict key, including the tuple-containing-a-list counterexample.
Hands-on drill · 30 min
Build a log triage tool with a hand-computable fixture, then explain the environment and Git state that make it reproducible. Unlike randomly sampling 5,000 rows, a deterministic fixture gives exact expected counts and does not confuse likely outcomes with guarantees.
Step 1 — environment (5 min)
In a new disposable directory, inspect python3 --version and python3 -c 'import sys; print(sys.executable)'. Run python3 -m venv .venv once, then use .venv/bin/python explicitly; activation is only a PATH convenience. Record the interpreter version in your notes and put .venv/, __pycache__/ and generated logs in .gitignore. Recreating a venv does not recreate OS packages or prove portability. A setup script should check preconditions and stop on failures, not reinstall system tools indiscriminately.
WSL 2 runs a real Linux kernel in a managed VM; WSL 1 uses a compatibility architecture. Choose from actual compatibility requirements. For Linux tools, Microsoft's filesystem guidance favors storing the project in the Linux filesystem; that is not an unconditional rule for Windows-native tooling or a measured slowdown for this exercise. Diagnose CRLF with a byte/text inspection before changing line endings; a venv cannot fix line endings or filesystem placement.
Step 2 — generate a log file to work against (5 min)
Save this as triage.py. Running it prints ten synthetic combined-log-style rows; redirect output to access.log when following the shell exercise. This fixture has no spaces inside request paths, so whitespace fields 7, 9 and 10 are path, status and bytes. A general web-log parser must handle the actual format rather than assuming these positions for every log source.
from collections import Counter, defaultdict
records = [("10.0.0.1", "/users", 200, 100)] * 4
records += [("10.0.0.2", "/orders", 500, 300)] * 3
records += [("10.0.0.1", "/health", 200, 10)] * 3
lines = [f'{ip} - - [01/Jan/2024:12:00:00 +0000] "GET {path} HTTP/1.1" {status} {size}'
for ip, path, status, size in records]
def summarize(lines):
statuses, paths, sizes = Counter(), Counter(), defaultdict(int)
for line in lines:
fields = line.split()
statuses[int(fields[8])] += 1
paths[fields[6]] += 1
sizes[fields[0]] += int(fields[9])
return statuses, paths, dict(sizes)
assert summarize(lines) == (
Counter({200: 7, 500: 3}),
Counter({"/users": 4, "/orders": 3, "/health": 3}),
{"10.0.0.1": 430, "10.0.0.2": 900},
)
assert summarize([]) == (Counter(), Counter(), {})
print("\n".join(lines))The byte sum for the first IP is 4×100 + 3×10 = 430; the second is 3×300 = 900. The most frequent IP need not transfer the most bytes.
Step 3 — answer three questions with shell only (10 min)
The following commands are a worked shell solution, not evidence that your shell has executed them. Save them in triage.sh and compare their results with the Python assertions. sort groups equal statuses before uniq -c; uniq alone counts adjacent runs. The final sort orders by descending count and then lexical value so ties are deliberate.
#!/usr/bin/env bash
set -euo pipefail
LOG="${1:-access.log}"
awk '{print $9}' "$LOG" | sort | uniq -c | sort -k1,1nr -k2,2
awk '{print $7}' "$LOG" | sort | uniq -c | sort -k1,1nr -k2,2
awk '{bytes[$1] += $10} END {for (ip in bytes) print bytes[ip], ip}' "$LOG" | sort -k1,1nr -k2,2Expect status rows (7,200),(3,500), paths (4,/users),(3,/health),(3,/orders) and bytes (900,10.0.0.2),(430,10.0.0.1). The pipeline connects stdout of each stage to stdin of the next; stderr is not piped here. These commands normally run external processes; shells may optimize builtins differently. -e has context-dependent exceptions, -u rejects unset variable expansion, and -o pipefail makes pipeline status the rightmost nonzero stage status (or zero if all stages succeed). It does not stop downstream stages or roll back their output. They are three options, not four blanket safety guarantees. grep 500 can match a byte count or URL: parse the status field instead.
Failure check: with pipefail enabled, (exit 7) | (exit 3) | cat returns 3, not 7: the rightmost failing stage determines status. A producer may fail after emitting data, so a successful-looking downstream output alone does not prove a successful pipeline. This is independent of -e, which decides whether the shell exits in the current context.
Step 4 — the Python half (7 min)
The two names below first share a list. List += mutates that list; + constructs another list and assignment rebinds only one name. The assertions distinguish equality from identity and demonstrate both mutable-default repairs.
def broken(item, cart=[]):
cart.append(item)
return cart
def fresh(item, cart=None):
if cart is None:
cart = []
cart.append(item)
return cart
def persistent(item, cart=()):
return cart + (item,)
first = broken("a")
second = broken("b")
assert first is second and first == ["a", "b"]
assert fresh("a") == ["a"] and fresh("b") == ["b"]
assert persistent("a") == ("a",) and persistent("b") == ("b",)
a = [1, 2]; b = a; b += [3]
assert a == [1, 2, 3] and a is b
b = b + [4]
assert a == [1, 2, 3] and b == [1, 2, 3, 4] and a is not bThe tuple-default function returns a new value; it does not append to the immutable default. A tuple is hashable only when all of its elements are hashable: (1,2) can be a key, but ([1],2) cannot. int, str, bytes and frozenset are immutable; lists, dicts and sets are mutable. A tuple's references are fixed, but a referenced list may still mutate.
Step 5 — commit it (3 min)
In the disposable exercise repository, use git init, inspect git status, then stage only .gitignore, triage.py and triage.sh. Inspect git diff --cached before making a local commit; inspect git show --stat HEAD afterward. No remote push is required. A commit records a tree reference, parent references and metadata. A branch is a movable reference, not necessarily a loose file: packed references and other storage backends exist. A remote is named configuration, including fetch/push URLs and refspecs. Packfiles may delta-compress objects even though the logical model is snapshots.
For three commits made on the wrong branch, first create a new branch at the current tip (git branch recovered-work HEAD) so the commits remain reachable. Run git switch recovered-work, then git log --oneline -3 and git status to verify the tip and worktree. These commands preserve reachability; they do not remove commits from the old branch. Moving the original branch back is a separate potentially destructive operation requiring a clean worktree and confirmation that the commits were not shared; it is not necessary to save the work. For a lost local tip, inspect git reflog, then create a branch at the verified old commit ID. Reflogs expire and are local: they are not backups or a guarantee of recovery after garbage collection.
Documentation lookup and transfer check
For learning a new tool, start with a tutorial; for a concrete task use a how-to; for the meaning of a flag use reference documentation; for the design rationale use explanation. Local --help and man should match the installed version. Man-page headings vary; start with NAME/SYNOPSIS and the relevant OPTIONS, then check exit-status and caveat sections rather than memorizing a universal order. An official-domain query narrows search results, but still check version and applicability. If reference and observed behavior disagree, build a minimal reproduction and check release notes/source for that version; a random source checkout does not automatically describe your binary.
Retest: add a path containing 500 with status 200. The status tally must remain field-based. Add a tuple containing a list to a set and explain the resulting TypeError. For the environment, demonstrate a second independent venv using the same declared interpreter rather than treating successful activation as reproducibility proof.
Environment specification and shell expansion answer key
A repeatable setup declares the interpreter and dependencies; it also checks assumptions it cannot repair. This stdlib-only exercise needs no third-party packages. Here is the missing setup.sh skeleton for a new disposable exercise directory, not your existing project. It deliberately refuses an existing venv with a different interpreter version instead of silently replacing it. Re-running it does not append duplicate ignore rules or install system packages.
#!/usr/bin/env bash
set -euo pipefail
command -v python3 >/dev/null
version=$(python3 -c 'import platform; print(platform.python_version())')
if [[ ! -d .venv ]]; then
python3 -m venv .venv
fi
actual=$(.venv/bin/python -c 'import platform; print(platform.python_version())')
if [[ "$actual" != "$version" ]]; then
printf 'venv version %s differs from requested %s\n' "$actual" "$version" >&2
exit 1
fi
.venv/bin/python -c 'import sys; assert sys.prefix != sys.base_prefix'
printf 'Interpreter: %s\n' "$actual"
# Add .venv/, __pycache__/ and access.log once to your exercise .gitignore.This checks version equality, not identical OS libraries or reproducible wheels. A dependency-bearing project also needs recorded dependency versions and a compatible installation method. WSL filesystem placement is an I/O choice; CRLF is a text-format choice; package isolation is a venv choice. None implies the other two. A version check plus a small functional check is stronger than trusting an activated prompt.
Git's working tree holds edited files; git add path copies the selected content into the index; git commit records the index as a new commit. git diff compares worktree to index; git diff --cached compares index to HEAD. Before recovery, inspect git status and git reflog; create git branch recovered-work <verified-commit-id> and then inspect git log recovered-work. The angle-bracket token is a placeholder to replace, not literal shell syntax. Keeping a branch at the tip saves all its reachable parent commits. Removing those commits from the old branch is not needed for recovery.
Bash expands an unquoted glob before invoking an external command; quoting "*.log" passes a literal asterisk. With default Bash settings a glob matching no filenames remains literal (options such as nullglob/failglob change this). Unquoted parameter expansion can undergo word splitting and globbing; "$LOG" keeps a path containing spaces as one argument. Brace expansion uses {a,b}, not {a, b}. With cat access.log | grep 500 | wc -l, the three external commands normally run as three processes; pipe endpoints connect fd 1 to fd 0, while fd 2 remains stderr. A grep exit status of 1 means no matches, which can fail a pipefail pipeline without indicating an I/O error. -e is suppressed in contexts such as an if test; it is not exception handling.
Worked lookup: to learn git log --follow, try git log -h then git help log, or search site:git-scm.com/docs/git-log --follow. Check the documented single-path restriction against the installed version. For an unfamiliar workflow use a tutorial, for a specific task a how-to, for the flag reference, and for the snapshot rationale an explanation. If the example fails, retain the command, version and minimal input before investigating version-specific documentation.
Transfer solution: with x=[1,2,3]; y=x, draw both arrows to one list. y.append(4) makes both names read [1,2,3,4]; z=x.copy() creates another outer list but shares any nested objects. hash(([1],2)) raises TypeError because the list element is unhashable. The status parser still counts a status-200 /500 request as 200 because it reads field 9, not a substring.
Answer constraints and corrections
A venv isolates packages, not the OS. Activation is optional. Git commits reference snapshots; packfiles may delta-compress them. uniq counts adjacent runs, so grouping first is necessary. With a=[1,2]; b=a; b += [3], both names see the mutation. Rebinding b=b+[3] instead leaves a unchanged.
Gap analysis + next week preview · 10 min
Score each original recall question separately: explain unaided; explain with a hint; or not yet. A fluent verbal answer is not a successful implementation. Choose one failed concept, write a counterexample that distinguishes the mistaken rule from the correct one, and schedule an unseen retest. Keep successes as well as failures in the log so the plan does not become a list of increasingly familiar problems.
For the drill, require an explicit input contract, a correctness argument or domain assumption, one ordinary case, one boundary case and an independent expected result. A chart, compile success or passing sample alone does not meet that rubric. Use the stated input contracts and named runtime when interpreting a result; finite tests and timing samples are not universal correctness or performance guarantees.
Next week (S006–S010) moves from environment into Python proper: control flow and comprehensions, functions with arguments, scope, and closures, then the data structure decision table — list versus tuple versus dict versus set — followed by classes and objects, and finally inheritance, composition, and polymorphism. The reference model you drilled in S005 is the foundation for all of it: closures capture names, and mutable default arguments are just one instance of a much more general trap.
Corrective source notes
- venv — Creation of virtual environments — Python 3.14.8 documentation — Virtual environments isolate installed Python packages, not operating-system state. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
- Comparing WSL Versions | Microsoft Learn — WSL 2 uses an actual kernel in a managed VM; choose by compatibility rather than an unconditional rule. Filesystem placement should match the tools operating on the project. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
- Git - Git Objects — Commit objects record snapshot tree, parent references and metadata; they are not bare diffs. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
- Git - Packfiles — Packfiles can delta-compress logical objects; snapshot semantics do not imply no stored deltas. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
- Git — git-pack-refs — The description explains loose refs versus the packed-refs file; a branch is not necessarily one loose file. Raw manual and governing passage retrieved and reviewed 2026-10-09.
- Git - git-reflog Documentation — Reflogs record reference updates in the local repository, supporting verified-tip recovery. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
- The Set Builtin (Bash Reference Manual) — errexit has contextual exceptions, including an if test. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
- Shell Expansions (Bash Reference Manual) — Word splitting and filename expansion are shell expansion stages; quote variable paths. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
- Diátaxis — Documentation has distinct tutorial, how-to, reference and explanation forms. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
- 4. More Control Flow Tools — Python 3.14.8 documentation — Mutable defaults are shared across omitted-argument calls; None constructs a fresh value. Source retrieved 2026-10-07; passage and applicability reviewed 2026-10-07.
The cited passages support these named claims. The algebra, counterexamples and executable fixtures supply the lesson-specific reasoning; finite checks are not universal benchmarks.
Pause / Recall / Apply
Can you explain it without the page?
Close the example. Reconstruct the core idea, then change one assumption. Mark complete when you’re ready; you can always undo it.
Stored in this browser only. No account, no sync. Clearing browser data removes your record.