Python errors are exceptions, not return codes. Test with pytest; lint/format with ruff + black in modern repos.
Core concepts
- try/except/else/finally: Catch specific exceptions; avoid bare
except:. — Python docs: Errors and exceptions - Custom exceptions: Subclass
Exceptionwith clear names. — Python docs: User-defined exceptions - pytest:
assertdriven tests; fixtures for DB/API setup. — pytest getting started - logging:
logging.getLogger(__name__)— not print in production. — Python docs: Logging HOWTO - Static typing: Gradual typing with mypy/pyright on CI. — mypy getting started
import pytest
def divide(a: int, b: int) -> float:
if b == 0:
raise ValueError("b must be non-zero")
return a / b
def test_divide():
assert divide(10, 2) == 5
with pytest.raises(ValueError):
divide(1, 0)Concept videos
YouTube lessons for these topics. On a large screen they open in the reading panel next to the roadmap, same as the article links on each core concept.
- Type hints — Python Type Hints (ArjanCodes)
Explanation
Exceptions are red flags on the playfield — they stop normal flow and jump to a handler. Testing and logging turn chaos into observable, fixable systems.
try/except/else/finally
Catch specific exceptions (ValueError, HTTPError) instead of bare except: which hides bugs. else runs when no exception occurred; finally always runs for cleanup. Structure mirrors real workflows: try the risky step, handle known failures, always close resources.
Custom exceptions
Domain errors (PaymentDeclined, InvalidToolArguments) make logs searchable and APIs clearer than generic Exception. Subclass Exception, not BaseException, unless you know why.
pytest
pytest discovers tests automatically, uses plain assert, and supports fixtures for shared setup (database, HTTP client). Parametrize tests to cover edge cases without copy-paste.
logging
print disappears in production log aggregators. The logging module levels (DEBUG, INFO, WARNING) let you tune verbosity. Include context (request id, user id) in messages for tracing.
Static typing
Type hints document contracts; mypy/pyright catch mismatches before runtime. Gradual typing means you can annotate hot paths first without rewriting the entire codebase.
What Is It?
Python signals errors with exceptions propagated up the stack until handled.
pytest discovers tests, asserts outcomes, and supports fixtures for setup/teardown.
ruff lints quickly; black formats; mypy/pyright optional for types.
Why Should I Care?
Untested exception paths kill production agents when tool calls fail.
Consistent formatting keeps diffs readable in GitHub.
Real World Analogy
pytest is a spellchecker for behavior — it underlines sentences that no longer mean what you think after an edit.
Visual Mental Model
ruff/black -> pytest -> CIBeginner Explanation
Raise specific exceptions, log context, and test with pytest fixtures. Let ruff and black handle style so reviews focus on logic.
Smallest Possible Example
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("b must be non-zero")
return a / bCommon Mistakes
❌ Bare except: catching everything including KeyboardInterrupt.
Catch specific exceptions; let process signals through.
❌ Tests that depend on execution order.
pytest runs tests in isolation — share state via fixtures, not globals.
❌ Skipping logging configuration in services.
Structured logs are how you debug prod without pdb.
Real Project Example
GitHub Actions runs pytest on most Python open source.
Sentry captures stack traces from raised exceptions in FastAPI apps.
Build This Mini Project
Add pytest tests for a function's happy path and ValueError path; run ruff check and black on the folder.
Interview Question
How do pytest fixtures help?
Fixtures provide reusable setup (DB connections, temp files, clients) injected into tests, keeping tests small and avoiding duplicate boilerplate.
Key Takeaways
- Raise meaningful exception types.
- pytest for automated behavior checks.
- Lint/format in CI.
- Log structured context in services.
Resources
The 60-second story
A cashier’s drawer jams. You do not flip the whole store’s power. You deal with a jammed drawer. try is the sale. except ValueError is “this specific jam.” A bare except: is catching every problem including “the building is on fire” and then smiling. else runs when nothing jammed. finally closes the drawer either way.
Custom exceptions are name tags on the jam. class DrawerJam(Exception) tells the next person which aisle. Exception as a class name in a log is how on-call engineers lose an hour.
pytest is a friend who walks in before opening and says “charge $4.50 for a coffee” and fails the build if you charge $45 because of a decimal slip. Fixtures are the pre-opened drawer and the fake card reader so the test does not call a real bank.
print is shouting in the kitchen. logging.getLogger(__name__) is a logbook with levels. Production can keep warnings and hide the play-by-play. Prints stay in the binary until someone is embarrassed.
Type checkers are the person who reads the menu labels. mypy and pyright do not stop Python from running a lie. They stop the lie from merging when you ask them to. Gradual typing means you label the hot aisle first, not the whole mall in a weekend.
Map the store.
try/exceptis the sale and the specific jam.- A bare
exceptis ignoring the fire alarm. - A custom exception is a name tag.
- pytest is the friend before opening.
- A fixture is the prepped drawer.
- logging is the logbook.
- mypy or pyright is the label check.
class DrawerJam(Exception):
pass
def charge(amount):
if amount < 0:
raise DrawerJam("negative charge")
return amountThe test is one line of intent: assert charge(4.5) == 4.5. If it raises, pytest shows you the name tag instead of a novel.
These tools exist because “it worked when I clicked” is not a system. Exceptions were shaped so you can catch the jam you understand and let the rest crash loudly. Loud crashes get fixed. Swallowed crashes become folklore.
Mental model: name the jam, write it in the logbook, and let a friend try the sale before customers do.
You have the page when bare except feels rude, and when you would rather see a failing test on your laptop than a polite empty catch in production. If a teammate asks why the logbook matters, tell them a print disappears into the noise and a named jam does not.
