# מערך שיעור: Exceptions ו-Logging<no value>

**משך:** יום לימוד מלא — 6 שעות אקדמיות (270 דקות).

**מטרת השיעור:** הסטודנטים יבינו שכשלים הם חלק טבעי מתוכנה, ויידעו לכתוב קוד שמזהה בעיות (raise), מטפל בהן (try/except) ומתעד אותן (logging), במקום לקרוס או להסתיר אותן.

**בסוף השיעור הסטודנטים צריכים:**

1. לכתוב try/except/else/finally ולחזות במדויק אילו בלוקים ירוצו בכל תרחיש.
2. לזרוק `ValueError`/`TypeError` עם הודעה ברורה כדי לבצע Validation בפונקציה, ולתפוס אותן אצל מי שקרא.
3. להגדיר Logging לקובץ עם רמות מתאימות, ולרשום שגיאות עם Traceback בעזרת `logging.exception`.

**דגש קריטי:** **"הפונקציה מזהה וזורקת, מי שקרא מחליט."** זה העיקרון שמחבר Exceptions ל-OOP (Validation במחלקות, נושא OOP) ול-FastAPI (שגיאה → תשובת HTTP, נושא Backend ומסדי נתונים).

---

## מהלך השיעור

| חלק בשיעור | משך | הערות ודגשים |
|---|---|---|
| פתיחה: תוכנה שקורסת | 10 דק' | המרצה מריץ מחשבון קטן ושולח לו "hello" — והוא קורס. "איך מונעים את זה?" |
| try / except | 40 דק' | `safe_age.py`, ואז `safe_divide.py`. להדגים את הסכנה של `except:` ריק (Ctrl+C לא עובד!). |
| else / finally | 35 דק' | "חזה את הפלט" על הלוח. דוגמת החיבור עם return. להדגיש: finally רץ גם אחרי return. |
| הפסקה | 15 דק' | |
| raise | 40 דק' | למה print/None גרועים. `withdraw` → `atm_safe`. Validation בהרשמה. |
| Logging א': print מול logging, רמות | 35 דק' | `log_levels.py`: לשנות רמה ולראות את הסינון. "בחר רמה" בכיתה. |
| הפסקה | 15 דק' | |
| Logging ב': קובץ, logging.exception | 35 דק' | `payments.py` + `tail -f` / `Get-Content -Wait` בשני Terminals. |
| תרגול + בדוק את עצמך | 45 דק' | Mini Project "מעבד הזמנות עמיד". |

---

## דגשים להעברת השיעור

- **להתחיל מכאב אמיתי.** שרת שקורס בגלל לקוח אחד הוא מוטיבציה טובה יותר מכל הגדרה.
- **"try קטן"** — לחזור על זה כל הזמן. סטודנטים נוטים לעטוף פונקציה שלמה ב-try.
- **שאלת "האם לתפוס כאן?"** — בדוגמה של `divide` שמחזירה 0: לעצור ולשאול את הכיתה אם זו תשובה נכונה. מכאן לעבור באופן טבעי ל-`raise`.
- **הודעות שגיאה הן UX למפתחים.** לבקש מהסטודנטים לשפר הודעה גרועה ("error") להודעה טובה (כלל + ערך שהתקבל).
- **Logging מול print:** החלוקה היא "למשתמש" לעומת "למפתח". להראות את שניהם באותה תוכנית (`payments.py`).
- **מוסכמת `%s` ב-logging:** להסביר בקצרה למה (ביצועים), אבל לא להתעכב. f-strings לא נלמדו עדיין, ולכן לא להציג אותם כחלופה.
- **אבטחה:** הדגש "לא לרשום סיסמאות" הוא הרגל מקצועי חשוב. כדאי להזכיר מקרים מפורסמים של דליפות מלוגים.
- **dict ו-tuple** מופיעים היום בקצרה (`catalog`, `for name, age in ...`). להגיד במפורש שמחר נלמד אותם כמו שצריך.

---

## מושגים

| מושג | הסבר קצר |
|---|---|
| Exception | אובייקט שגיאה ש-Python זורקת בזמן ריצה. |
| Raise / Throw | זריקת שגיאה. |
| Catch | תפיסת שגיאה עם `except`. |
| try / except / else / finally | נסה / אם נכשל / אם הצליח / תמיד. |
| `as error` | גישה לאובייקט השגיאה ולהודעה שלו. |
| Validation | בדיקת תקינות של קלט לפני השימוש בו. |
| Re-raise | `raise` לבד בתוך except, שמעביר את אותה שגיאה הלאה. |
| ValueError / TypeError | ערך לא חוקי / טיפוס לא נכון. |
| Log / Log Record | יומן אירועים / רשומה בודדת בו. |
| Log Level | DEBUG, INFO, WARNING, ERROR, CRITICAL. |
| basicConfig | הגדרה חד-פעמית של רמה, פורמט ויעד. |
| filemode a / w | הוספה לקובץ / דריסה. |
| logging.exception | רשומת ERROR עם Traceback מלא. |

---

## תרגיל חשיבה

**שאלת פתיחה (לפני `raise`):** "כתבתי פונקציה שמחשבת הנחה: `apply_discount(price, percent)`. מישהו קרא לה עם `percent=150`. מה הפונקציה צריכה לעשות?"

**מהלך:**

1. לאסוף אפשרויות על הלוח: להחזיר 0, להחזיר את המחיר המקורי, להדפיס שגיאה, להחזיר `None`, להגביל ל-100, לזרוק שגיאה.
2. לכל אפשרות לשאול: "מה יקרה למי שקרא, אם הוא לא שם לב?" (מחיר שלילי בעגלה, מוצר בחינם, `None` שגורם לקריסה 50 שורות אחר כך).
3. לשאול: "מי יודע מה נכון לעשות: הפונקציה, או המסך/השרת שקרא לה?"
4. להוביל ל: הפונקציה **מזהה** (150 לא חוקי), ומי שקרא **מחליט** (הודעה למשתמש, תשובת HTTP 400, לוג).

**מה המרצה מראה בסוף:** אותה `apply_discount` עם `raise ValueError("percent must be 0-100, got 150")`, ושני "קוראים" שונים: אחד מדפיס הודעה ידידותית, השני רושם ללוג ומחזיר שגיאה. משפט סיום: "שגיאה שנזרקת בקול עדיפה על באג שקט — את הראשונה רואים מיד, את השני מגלים אחרי חודש."
