Skip to content
VLSI Mentor

Python Part I · Module 10 · Loops

Stopping Early: break, continue and the First Failure

A loop that always runs to the end of its collection is the right shape for counting things and the wrong shape for finding one. A twenty-gigabyte simulation log may contain a single UVM_FATAL on line forty, and everything after it is a consequence of that fatal rather than new information — so reading the remaining nineteen gigabytes answers nothing and costs minutes. The same loop may also want to ignore comment lines entirely, dealing only with the rows that carry data. Python has one statement for each of those: one that leaves the loop, and one that abandons the current pass. They are small and they change the shape of what a for loop can express.

Foundation16 min readPythonPart IFundamentalsLoopsControl Flow

Part I · Module 10 · Chapter 10.7 · Stopping Early: break, continue and the First Failure

1. Finding Is Not Counting

Here is a log and a question that does not need all of it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
log = [
    "UVM_INFO  @0: reset asserted",
    "UVM_FATAL @120: bus protocol violation",
    "UVM_INFO  @200: shutting down",
    "UVM_ERROR @210: no response",
]

for line in log:
    if "UVM_FATAL" in line:
        print("first fatal:", line)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
first fatal: UVM_FATAL @120: bus protocol violation

The answer is right, and the loop kept reading after it had it. With four lines that costs nothing. With four million it costs the whole scan, and the lines after a fatal are usually the simulator unwinding rather than independent evidence.

(As in Chapter 10.2, the list stands in for a file you would normally read. Opening one is Chapter 15.2; the loop is identical either way.)

2. break Ends the Loop

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
log = [
    "UVM_INFO  @0: reset asserted",
    "UVM_FATAL @120: bus protocol violation",
    "UVM_INFO  @200: shutting down",
    "UVM_ERROR @210: no response",
]

for line in log:
    if "UVM_FATAL" in line:
        print("first fatal:", line)
        break

print("scan stopped")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
first fatal: UVM_FATAL @120: bus protocol violation
scan stopped

break ends the loop immediately. Execution continues at the first line after the loop body — here, print("scan stopped").

3. What break Actually Skips

Two things, and the second is the one that matters operationally:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
log = ["INFO a", "UVM_FATAL b", "INFO c", "UVM_ERROR d"]

scanned = 0

for line in log:
    scanned += 1
    if "UVM_FATAL" in line:
        first_fatal = line
        break
    print("checked:", line)

print("stopped after", scanned, "of", len(log), "lines")
print("first fatal:", first_fatal)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
checked: INFO a
stopped after 2 of 4 lines
first fatal: UVM_FATAL b

checked: INFO a printed once, not twice — so break skipped the rest of the body on the pass it ran in. And scanned reached 2, not 4 — so it also skipped the rest of the collection.

That second number is the point. The loop did half the work. On a log where the fatal is at line 40 of 4,000,000, it does 0.001% of the work, and the saving is the difference between a report that returns immediately and one that reads twenty gigabytes to tell you something it knew in the first second.

4. continue Skips the Rest of One Pass

continue does not end the loop. It abandons the current pass and goes straight to the next item:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lines = ["# header", "axi_smoke PASS", "", "# note", "axi_burst FAIL"]

kept = 0

for line in lines:
    if line.startswith("#"):
        continue
    if not line.strip():
        continue
    kept += 1
    print(kept, line)

print("kept", kept, "of", len(lines))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 axi_smoke PASS
2 axi_burst FAIL
kept 2 of 5

Five passes happened. Three of them reached a continue and went no further; two ran the whole body. The loop still visited every item — continue is about the body, not about the collection.

The two guards read as a list of reasons to ignore a line, each stated once at the top, which is exactly how you would describe the rule in words: skip comments; skip blanks; otherwise, process it.

5. continue or an if?

The same filter can always be written as a condition around the body:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lines = ["# header", "axi_smoke PASS", "", "# note", "axi_burst FAIL"]

kept = 0

for line in lines:
    if not line.startswith("#") and line.strip():
        kept += 1
        print(kept, line)

print("kept", kept, "of", len(lines))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 axi_smoke PASS
2 axi_burst FAIL
kept 2 of 5

Same output. Which is better depends on how many reasons there are and how long the body is.

  • One simple reason and a short body → the if is clearer. The condition says what you want, positively.
  • Several unrelated reasons, or a long body → continue guards are clearer. Each exclusion is stated on its own line, and the body is not indented inside a condition that may itself be compound and negated.

The if version above already shows the strain: not ... and ... mixes a negative with a positive, and adding a third exclusion makes it worse. Chapter 9.3 warned about exactly that shape.

6. pass Does Nothing — and Is Not a Quiet continue

pass is a statement that does nothing at all. It exists because Python requires a block to contain at least one statement, as Chapter 9.2 showed when an empty if raised IndentationError:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for test in ["smoke", "reset"]:
    if test == "smoke":
        pass
    else:
        print("handling", test)

print("done")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
handling reset
done

The pass branch is a deliberate "nothing to do here, and that is intentional" — more honest than omitting the branch, because a reader can see the case was considered.

It is not a continue. The same loop with each makes the difference unmissable:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print("--- continue ---")
for n in [1, 2, 3]:
    if n == 2:
        continue
    print("body end for", n)

print("--- pass ---")
for n in [1, 2, 3]:
    if n == 2:
        pass
    print("body end for", n)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
--- continue ---
body end for 1
body end for 3
--- pass ---
body end for 1
body end for 2
body end for 3

continue skipped the rest of the body on the second pass, so 2 is missing. pass did nothing, so execution carried straight on and 2 printed like every other value.

7. break Exits One Loop

When a loop contains another, break ends only the loop it is written in — the innermost one:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for test in ["smoke", "reset"]:
    for seed in [1, 2, 3]:
        if seed == 2:
            break
        print(test, seed)

print("done")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
smoke 1
reset 1
done

The inner loop broke at seed == 2 both times, and the outer loop carried on to its second test regardless. There is no statement that breaks out of two loops at once. Chapter 10.11 covers nesting properly, including what to do when you genuinely need to leave both.

8. Both Statements in One Scan

The canonical use, with the two doing their separate jobs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
log = [
    "# simulation log",
    "UVM_INFO  @0: reset asserted",
    "",
    "UVM_ERROR @120: crc mismatch",
    "UVM_FATAL @130: bus protocol violation",
    "UVM_INFO  @200: shutting down",
]

errors_before_fatal = 0
first_fatal = ""

for line in log:
    if line.startswith("#") or not line.strip():
        continue
    if "UVM_FATAL" in line:
        first_fatal = line
        break
    if "UVM_ERROR" in line:
        errors_before_fatal += 1

print("errors before the fatal:", errors_before_fatal)
print("first fatal:", first_fatal)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
errors before the fatal: 1
first fatal: UVM_FATAL @130: bus protocol violation

continue removes the lines that are not data. break stops the scan once the question is answered. The order of the three checks is a branch-order decision of exactly the kind Chapter 9.1 covered — the continue guards come first because a comment line should not be counted as anything.

9. break in a while Loop

break works in both statements. In a while, it is an exit that does not go through the condition:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
polls = ["RUNNING", "FAILED", "DONE"]

checks = 0

while checks < 10:
    state = polls[checks]
    checks += 1
    if state != "RUNNING":
        print("settled on", state, "after", checks, "checks")
        break

print("loop over")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
settled on FAILED after 2 checks
loop over

This is the bounded poll from Chapter 10.5 with the success exit moved out of the condition and into a break. Both forms are fine; the break version keeps the condition as the pure safety bound, which some readers find clearer.

10. Which Exit Did It Take?

Here is the problem break creates. After the loop, how do you know whether it found something or simply ran out?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "burst"]

found = False

for test in tests:
    if test == "reset":
        found = True
        break

print("found:", found, "| loop variable:", test)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
found: True | loop variable: reset

The flag works. It is also three extra lines of bookkeeping — declare it, set it, check it — for a question the loop already knows the answer to.

And the loop variable is not a reliable substitute. It holds "reset" here because the loop broke there; had nothing matched, it would hold "burst" — the last item — which looks exactly like a successful find of the last item. Chapter 10.1's Section 4 is the reason, and relying on it is a bug Chapter 10.12 returns to.

11. Predict, Run, Observe

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# A
for n in [1, 2, 3, 4]:
    if n == 3:
        break
    print("A:", n)

# B
for n in [1, 2, 3, 4]:
    if n == 3:
        continue
    print("B:", n)

# C
count = 0
for n in [1, 2, 3, 4]:
    count += 1
    if n == 2:
        break
print("C: passes =", count)

# D
for n in [1, 2]:
    if n == 1:
        pass
    print("D:", n)

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
A: 1
A: 2
B: 1
B: 2
B: 4
C: passes = 2
D: 1
D: 2

A — stops entirely at 3, so only 1 and 2 print.

B — skips only 3; 4 still prints, because the loop continued.

C — two passes. The count += 1 on the breaking pass still ran, because it is before the break.

D — pass changed nothing; both values print.

12. Exercises

Exercise 1 — first match. Given widths = [1, 8, 0, 32], print the first width that is zero and stop. Print how many items were examined, and verify it is 3.

Exercise 2 — break or not. For each, say whether break belongs: (a) count the failing tests (b) report whether any test failed (c) find the seed of the first failure (d) total the runtimes (e) check whether a signal name appears anywhere in a netlist list.

Exercise 3 — rewrite the guards. Convert Section 4's two continue guards into one if around the body. Then add a third exclusion — skip lines shorter than five characters — to both versions, and say which stayed readable.

Exercise 4 — pass versus continue. Write a loop over [1, 2, 3] that prints every number, with if n == 2: pass in it. Then change pass to continue and predict the new output before running. Explain the difference in one sentence.

Exercise 5 — the body before the break. In Section 3, scanned reached 2. Move scanned += 1 to the line after the if block and predict the new value. Verify, and explain what that says about where break takes effect.

Exercise 6 — nested break. Using the program in Section 7, add a line inside the outer loop but after the inner one that prints finished test <name>. Predict the output, verify, and say in one sentence what it proves about break.

Exercise 7 — the flag. Write a loop that searches ["a", "b", "c"] for "z" using a found flag and a break. Print the flag and the loop variable afterwards. Explain in two sentences why the loop variable is misleading here.

Exercise 8 — combine both. Scan a list of log lines, ignoring blank lines and lines beginning with //, counting WARNING lines, and stopping at the first ERROR. Print the warning count and the error line.

13. Summary

  • break ends the loop immediately — skipping both the rest of the body and the rest of the collection.
  • The second saving is the important one: a scan that stops at the first match does a fraction of the work.
  • Use break when a second match would not change your answer.
  • continue abandons the current pass and goes to the next item. The loop still visits everything.
  • continue guards beat one compound condition when there are several unrelated reasons to skip, or the body is long.
  • pass does nothing at all. It is not a quiet continue — the rest of the body still runs.
  • break exits one loop, the innermost one containing it.
  • break works in a while too, and lets the condition stay a pure safety bound.
  • After a loop that may have broken, a flag tells you which exit you took — and the loop variable does not, because it holds the last item either way.

The next chapter replaces that flag with something the loop statement already provides.

Where this fits

Part of the Python curriculum.