|
| 1 | +# Exercise 06 - Scope Station: Reactor Rescue (Scope and Namespaces) |
| 2 | + |
| 3 | +## Description |
| 4 | +Orbital Station Alpha is failing! Engineers rushed the code and left variables in the wrong places: some are trapped inside functions, some are missing the right keyword, and some are hiding Python's own tools. |
| 5 | + |
| 6 | +This is a **debugging game**. `scopes.py` is already written, but it is *almost* working. Your job in each level is to figure out **where each variable must live** (and which keyword, if any, it needs) so the system runs. |
| 7 | + |
| 8 | +Remember the question from the lesson: *"Where does this name live, and who needs to see it?"* |
| 9 | + |
| 10 | +> Do not rename the functions. The tests call them by name. You decide where the variables go. |
| 11 | +
|
| 12 | +--- |
| 13 | + |
| 14 | +## Levels |
| 15 | + |
| 16 | +### Level 1 - Power Grid (Local vs. Global) |
| 17 | +`show_banner()` crashes with a `NameError`. The station name is created inside `boot_system()`, but two functions need it. |
| 18 | +* **Your job**: make both `boot_system()` and `show_banner()` work. |
| 19 | +* **Expected**: `show_banner()` returns `"=== Orbital Station Alpha ==="`. |
| 20 | + |
| 21 | +> **Next step:** Run `python check.py 06`. Once Level 1 passes, remove the `@pytest.mark.skip` line above `test_level2` in `test_scopes.py`. |
| 22 | +
|
| 23 | +### Level 2 - Life Support (Enclosing Scope) |
| 24 | +`build_oxygen_monitor()` returns an inner function `read_level()`, but it can't find the oxygen value. |
| 25 | +* **Your job**: put `oxygen` where `read_level()` can find it by looking outward, and don't use a global. |
| 26 | +* **Expected**: the monitor returns `"Oxygen at 100%"`. |
| 27 | + |
| 28 | +> **Next step:** Unlock `test_level3`. |
| 29 | +
|
| 30 | +### Level 3 - Comms Array (Built-in Scope) |
| 31 | +`summarize_signals()` fails with `TypeError: ... object is not callable`. Something is hiding Python's built-in tools. |
| 32 | +* **Your job**: find what is shadowing the built-ins and fix it. |
| 33 | +* **Expected**: `"3 signals, strength 49"`. |
| 34 | + |
| 35 | +> **Next step:** Unlock `test_level4`. |
| 36 | +
|
| 37 | +### Level 4 - Reactor Core (`UnboundLocalError`) |
| 38 | +`add_bonus_power()` crashes with `UnboundLocalError`. |
| 39 | +* **Your job**: fix it by sending the value **in** with a parameter and sending the result **out** with `return`. The global `power` must stay `50`, and `global` is not allowed in this level. |
| 40 | +* **Expected**: `add_bonus_power(50)` returns `60`. |
| 41 | + |
| 42 | +> **Next step:** Unlock `test_level5`. |
| 43 | +
|
| 44 | +### Level 5 - Cargo Bay (Mutable Objects) |
| 45 | +`load_crate()` adds items to a list that vanishes when the function ends, and `get_manifest()` can't find it. |
| 46 | +* **Your job**: make both functions share the same list. You don't need `global`, because you only change the list's contents. |
| 47 | +* **Expected**: loading `"Titanium"` then `"Ice"` gives a manifest ending in `["Titanium", "Ice"]`. |
| 48 | + |
| 49 | +> **Next step:** Unlock `test_level6`. |
| 50 | +
|
| 51 | +### Level 6 - Alarm System (`global`) |
| 52 | +`trigger_alarm()` should count alarms in the global `alarm_count`, but crashes. |
| 53 | +* **Your job**: let the function rebind the global variable. |
| 54 | +* **Expected**: three calls return `1`, `2`, `3` (from a fresh start) and `alarm_count` ends up updated. |
| 55 | + |
| 56 | +> **Next step:** Unlock `test_level7`. |
| 57 | +
|
| 58 | +### Level 7 - Drone Memory (`nonlocal`) |
| 59 | +Each drone made by `make_drone_counter()` should remember how many scans it did, but `scan()` crashes. |
| 60 | +* **Your job**: let the inner function update the enclosing variable. |
| 61 | +* **Expected**: one drone counts `1, 2, 3`, and a second drone starts again at `1`. |
| 62 | + |
| 63 | +> **Next step:** Unlock `test_level8`. |
| 64 | +
|
| 65 | +### Level 8 - Diagnostics (`globals()` and `locals()`) |
| 66 | +Complete the three helper functions in the `LEVEL 8` section: |
| 67 | +* `list_local_names(a, b)`: return a **sorted list** of the names in the function's local namespace. |
| 68 | +* `is_global(name)`: return `True` if `name` exists in the global namespace. |
| 69 | +* `probe_hidden()`: try to read `secret_code` (it only exists inside `_make_secret()`). Return `True` if Python raises `NameError`. |
| 70 | + |
| 71 | +> **Next step:** Unlock `test_level9`. |
| 72 | +
|
| 73 | +### Level 9 - BOSS: Reactor Boot Sequence |
| 74 | +`run_station()` has **three** different scope bugs at once. Use everything you have learned. |
| 75 | +* **Expected**: `"Checks: 3 | Passed: 2 | Peak: 91"`. |
| 76 | + |
| 77 | +> **Next step:** If Level 9 passes, congratulations! You saved the station. |
| 78 | +
|
| 79 | +--- |
| 80 | + |
| 81 | +## How to Run the Tests |
| 82 | +Run the following command from the repository root: |
| 83 | +```bash |
| 84 | +python check.py 06 |
| 85 | +``` |
| 86 | + |
| 87 | +Or run directly with pytest: |
| 88 | +```bash |
| 89 | +pytest exercises/foundations/06_scopes/test_scopes.py -v |
| 90 | +``` |
| 91 | + |
| 92 | +--- |
| 93 | + |
| 94 | +## Resources & Notes |
| 95 | +Review the concepts covered in this exercise in: |
| 96 | +* [The Python Ledger Curriculum: Scope and Namespaces](https://thepythonledger.github.io/Docusaurus-engine/lessons/code-organization/scope-and-namespaces) |
0 commit comments