Repository navigation
Expand file tree
/
Copy path10_error_handling_pipeline.py
More file actions
104 lines (81 loc) · 3.8 KB
/
Copy path10_error_handling_pipeline.py
File metadata and controls
104 lines (81 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""Module 10 - Build a small resilient processing pipeline."""
# 1. Real-World Scenario
# A batch contains user records from an external source. Bad records should be reported,
# while valid records continue through the pipeline.
# 2. The Challenge
records = [
{'id': 1, 'email': 'ava@example.com'},
{'id': 2, 'email': ''},
{'id': 3, 'email': 'sam@example.com'},
]
# Normalize valid emails and collect errors without stopping the whole batch.
# 3. My Current Python Toolkit
# Functions isolate stages; exceptions represent invalid input; dictionaries carry results.
# 4. Think Before Coding
# Which errors are expected? Should one bad record stop all processing?
# 5. Try It Yourself
# Implement normalize_email and process_records.
# 6. Solution
def normalize_email(record):
email = record.get('email', '').strip().lower()
if not email or '@' not in email:
raise ValueError(f"invalid email for record {record.get('id')}")
return {'id': record['id'], 'email': email}
def process_records_a(items):
valid = []
errors = []
for item in items:
try:
valid.append(normalize_email(item))
except (KeyError, TypeError, ValueError) as error:
errors.append(str(error))
return {'valid': valid, 'errors': errors}
print('Version A:', process_records_a(records))
process_records_b = lambda items: process_records_a(items)
print('Version B:', process_records_b(records))
# The named functions are clearer here; a lambda adds no value for this multi-step behavior.
# 7. Trace It Visually
# record 1 -> normalize -> valid.
# record 2 -> ValueError -> errors, continue.
# record 3 -> normalize -> valid.
# 8. What Is Actually Happening Internally?
# try runs risky code; except handles selected errors; the loop continues after each handled error.
# 9. THE IMPORTANT PART - THIS BECOMES...
# Python: validate -> catch expected error -> continue
# Backend: request validation and consistent error responses
# Production: retries, dead-letter records, metrics, and alerts
# AI / LLM: parse uncertain model output and preserve failed items for review
# Advanced System: resilient event processing [LEARN LATER]
# 10. Future Code Example
def parse_tool_call(payload):
try:
if payload.get('tool') not in {'search', 'lookup'}:
raise ValueError('unsupported tool')
return {'ok': True, 'tool': payload['tool']}
except (AttributeError, KeyError, ValueError) as error:
return {'ok': False, 'error': str(error)}
print('Tool result:', parse_tool_call({'tool': 'search'}))
print('Tool error:', parse_tool_call({'tool': 'delete'}))
# 11. Mind-Bending Questions
questions = [
'Why catch only expected exception types?',
'What happens if the except block itself fails?',
'When should an error stop the entire batch?',
'How could retries accidentally duplicate an operation?',
'What information should an API error response expose?',
]
for number, question in enumerate(questions, 1):
print(f'Q{number}: {question}')
# Answers
print('Answers: broad catches hide bugs; failures in handlers still propagate; critical corruption should stop; retries need idempotency; expose safe, useful error details.')
# 12. Interview Connection
print('Interview: exception scope, validation, partial failure, error contracts, and idempotency.')
# 13. Common Mistakes
# WRONG: except: pass # hides programming bugs and loses evidence.
# CORRECT: catch the expected exception and record useful context.
# WRONG: return from the first error when the batch should continue.
# CORRECT: append the error and process the remaining records.
# 14. Small Extension
# Add retryable versus permanent errors, a maximum retry count, and a dead-letter list.
# 15. Connection Graph
# exception -> validation boundary -> API error contract -> resilient batch -> event/LLM processing