DedSec Project

UnboundLocalError — DedSec Project

Python UnboundLocalError: a local is read before assignment Reproduce Python decides at compile time that counter is local to broken because it is assigned in that function. The earlier…

Python UnboundLocalError: a local is read before assignment

Reproduce

counter = 10
def broken():
    print(counter)
    counter = counter + 1

broken()

Python decides at compile time that counter is local to broken because it is assigned in that function. The earlier read therefore raises UnboundLocalError even though a global counter exists. The exception is a subclass of NameError, but the name is known to be local and uninitialized.

Prefer explicit state

def increment(counter):
    return counter + 1

assert increment(10) == 11
assert increment(0) == 1

This pure function has no global mutation and can be reused safely. For closures that intentionally mutate enclosing state, nonlocal can be appropriate; global modifies a module binding and should be a deliberate design choice, not a quick error suppression.

Diagnose branches and exception paths

An assignment inside an if does not guarantee initialization:

def label(flag):
    if flag:
        result = "ready"
    return result

label(False) raises UnboundLocalError. Initialize result before the branch, return from every branch, or make the missing case explicit. Also check try/except blocks: an assignment in try may be skipped when an exception occurs. Inspect the full function for any assignment, including augmented assignment such as +=, which reads and writes the local.

Lab

Call label(True) and label(False), then rewrite the function so both cases return a string. Create a closure with nonlocal and compare it to increment. Document whether state should be returned, shared within a closure, or stored in an object. Add tests for every branch rather than relying on a single successful input.

Minimal branch contract

A function must initialize a variable on every path before reading it. In a deliberately failing test, make the assignment conditional on flag and call the function with False. Compare three valid redesigns: return immediately in each branch, give the variable an explicit default, or raise a descriptive exception on unsupported input. A default is not always correct: for a missing mandatory result, raising is better than silently returning stale state.