Exceptions interrupt ordinary control flow until a matching handler is found.
Catch the exception you can actually handle, close resources, and let unrelated
faults remain visible. A broad except Exception: return 0 can turn a broken
evaluator into a plausible zero-error result.
Success and failure paths must both leave the opened context.
def parse_count(text):
try:
value = int(text)
except ValueError as exc:
raise ValueError("count must be an integer") from exc
if value < 0:
raise ValueError("count must be nonnegative")
return value
print(parse_count("3"))with enters a context and always exits it, including when a read or parse
fails. File objects are context managers. try/finally also guarantees
cleanup; a return in finally can suppress an exception and should be avoided.
import io
stream = io.StringIO("first\nsecond\n")
with stream as handle:
print(handle.readline().strip())
print(stream.closed)Accepting an opener function lets a test inject a string stream or failing context manager. The function under test owns the context it opens. This is useful for local files without requiring real disk fixtures or network calls. We practise the simpler text protocol first; JSONL validation comes later.
An infrastructure failure is different from malformed numeric data and must not become an apparently valid zero score.
For the build, blank/comment lines are ignored. Every other line must be a finite float; NaN and infinity parse successfully with float(), so check finiteness explicitly. On a bad value report its 1-based physical line number, and ensure the stream is closed. A read error must propagate; it is not a malformed numeric row and should not silently disappear from the denominator.