בפייתון ניתן ללכוד את מה שמודפס למסך דרך print().
זה שימושי עבור בדיקות, דיבאג, לוגינג, או כדי לדכא פלט “רועש”.
נראה איך לעשות זאת באופן אלגנטי — תוך שימוש רק בספריית הסטנדרט וכמה שורות קוד בלבד.
In Questo Articolo
🧩 הבנת Context Managers בפייתון
מהו Context Manager?
Context manager מנהל משאבים בצורה בטוחה ומסודרת.
הוא מגדיר התנהגות של אתחול וניקוי סביב בלוק קוד.
דוגמה: ניהול קבצים
with open("file.txt", "w") as f:
f.write("שלום, עולם!")
✅ ניקוי אוטומטי: הקובץ ייסגר גם אם תתרחש חריגה.
למה Context Managers שימושיים
- ניקוי אוטומטי – משאבים משתחררים לבד
- בטיחות במקרה של שגיאות – הניקוי מתבצע גם אם מתרחשת חריגה
- קוד קריא יותר – לוגיקת setup/teardown במקום אחד
- ניתן לשימוש חוזר – מתאים לקבצים, locks, חיבורים לרשת או stdout
שימוש ב-@contextmanager מ-contextlib
הדקורטור @contextmanager מאפשר ליצור context managers בעזרת גנרטורים:
from contextlib import contextmanager
@contextmanager
def my_context():
print("נכנסים להקשר")
resource = "משאב כלשהו"
try:
yield resource
finally:
print("יוצאים מהקשר")
with my_context() as res:
print("בתוך הקשר:", res)
פלט:
נכנסים להקשר
בתוך הקשר: משאב כלשהו
יוצאים מהקשר
💡 הסבר:
- הקוד לפני
yield→ מתבצע בעת הכניסה - הערך המוחזר מ-
yield→ זמין כ-as res - הקוד אחרי
yield→ מתבצע ביציאה, גם אם התרחש חריגה
זה מושלם עבור שינוי זמני של מצב גלובלי, כמו תפיסת stdout.
⚡ סיכום: Context Managers עבור stdout/stderr
| משימה / תרחיש | Context Manager מומלץ |
|---|---|
תפוס רק את print | capture_stdout |
| תפוס רק שגיאות/חריגות | capture_stderr |
| תפוס stdout ו-stderr בו זמנית | capture_output |
| תפוס והצג בו זמנית | capture_stdout_tee / capture_output_tee |
📦 תפיסת stdout
from contextlib import contextmanager
import sys, io
@contextmanager
def capture_stdout():
old_stdout = sys.stdout
sys.stdout = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stdout = old_stdout
דוגמה:
with capture_stdout() as out:
print("הפלט הזה נלכד")
print("נלכד:", out.getvalue())
🔧 תפיסת stderr או שני הזרמים
תפיסת stderr
@contextmanager
def capture_stderr():
old_stderr = sys.stderr
sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stderr = old_stderr
תפיסת stdout + stderr
@contextmanager
def capture_output():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
🔀 מצב Tee: תפוס והצג בו זמנית
class Tee(io.StringIO):
def __init__(self, original):
super().__init__()
self.original = original
def write(self, text):
self.original.write(text)
super().write(text)
def flush(self):
self.original.flush()
Context managers:
@contextmanager
def capture_stdout_tee():
old_stdout = sys.stdout
sys.stdout = tee = Tee(old_stdout)
try:
yield tee
finally:
sys.stdout = old_stdout
@contextmanager
def capture_output_tee():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = tee = Tee(old_stdout)
try:
yield tee
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
דוגמה:
with capture_stdout_tee() as out:
print("נראה וגם נלכד")
print("תוכן Tee:", out.getvalue())
📦 קובץ מלא: stdout_capture.py
"""
כלי ללכידת stdout ו-stderr באמצעות context managers.
תומך ב:
- תפיסת stdout
- תפיסת stderr
- תפיסה משולבת
- מצב Tee
"""
import sys, io
from contextlib import contextmanager
@contextmanager
def capture_stdout():
old_stdout = sys.stdout
sys.stdout = buffer = io.StringIO()
try: yield buffer
finally: sys.stdout = old_stdout
@contextmanager
def capture_stderr():
old_stderr = sys.stderr
sys.stderr = buffer = io.StringIO()
try: yield buffer
finally: sys.stderr = old_stderr
@contextmanager
def capture_output():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = buffer = io.StringIO()
try: yield buffer
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
class Tee(io.StringIO):
def __init__(self, original):
super().__init__()
self.original = original
def write(self, text):
self.original.write(text)
super().write(text)
def flush(self):
self.original.flush()
@contextmanager
def capture_stdout_tee():
old_stdout = sys.stdout
sys.stdout = tee = Tee(old_stdout)
try: yield tee
finally: sys.stdout = old_stdout
@contextmanager
def capture_output_tee():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = tee = Tee(old_stdout)
try: yield tee
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
✅ דוגמאות שימוש
from stdout_capture import capture_stdout, capture_output, capture_stdout_tee
def test_func():
print("שלום מהפונקציה")
# תפיסת stdout
with capture_stdout() as out:
test_func()
print("stdout נלכד:", out.getvalue())
# תפיסת stdout + stderr
try:
with capture_output() as out:
print("משהו")
raise Exception("אופס!")
except Exception:
pass
print("פלט נלכד:", out.getvalue())
# תפיסת והצגת פלט בו זמנית
with capture_stdout_tee() as out:
print("נראה וגם נלכד")
print("תוכן Tee:", out.getvalue())
⚠️ טיפים ומלכודות
1️⃣ פלט גדול מאוד
io.StringIOעלול לצרוך הרבה זיכרון.- השתמש בקובץ זמני:
import sys, tempfile
with tempfile.TemporaryFile(mode='w+') as tmp:
old_stdout = sys.stdout
sys.stdout = tmp
try:
print("הרבה נתונים...")
tmp.seek(0)
captured = tmp.read()
finally:
sys.stdout = old_stdout
2️⃣ פלט תהליכים משניים
capture_stdout()לא תופס פלט מתהליכים חיצוניים.- השתמש ב-
subprocess.PIPE:
import subprocess
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
print(result.stdout)
3️⃣ Thread-safety
sys.stdoutו-sys.stderrהם גלובליים → הימנע מלכוד בו זמנית בכמה threads.
4️⃣ מתי להשתמש ב-Tee
- דיבוג פונקציות עם לוגינג
- כלים CLI אינטראקטיביים
- בדיקות אינטגרציה עם פלט למסוף
5️⃣ שחזור זרמים
- תמיד שחזר את
sys.stdout/sys.stderrב-finally
6️⃣ הימנע מהשכבות עומק גדולות
- לכידות מרובות עלולות לערבב פלט. השתמש בקונטקסט אחד לכל תרחיש.
7️⃣ Best Practices בבדיקות
- Unit tests →
capture_stdout()אוcapture_output() - דיבוג ידני → מצב Tee
- בדוק תוכן ה-buffer, לא תופעות לוואי