io — זרמי קלט/פלט

מודול זה מכיל סוגים נוספים של אובייקטי stream (דמויי קובץ) ופונקציות עזר. הוא חושף את הבילט-אין open() יחד עם חוצצי טקסט ובינאריים בזיכרון (StringIO, BytesIO) שמממשים את ממשק הזרם הסטנדרטי read/write/seek.

היררכיה רעיונית

הבדל מ-CPython

ההיררכיה הרעיונית של מחלקות הבסיס של הזרמים פושטה ב-MicroPython, כמתואר בסעיף זה.

מחלקות הזרם הבסיסיות (המופשטות), המשמשות יסוד להתנהגות של כל המחלקות הקונקרטיות, מצייתות ב-CPython למספר דיכוטומיות (סיווגים בזוגות). ב-MicroPython הן פושטו במידת מה ונעשו משתמעות כדי להשיג יעילות גבוהה יותר ולחסוך משאבים.

דיכוטומיה חשובה ב-CPython היא זרמים ללא חיץ לעומת זרמים עם חיץ. ב-MicroPython כל הזרמים כיום הם ללא חיץ. הסיבה היא שכל מערכות ההפעלה המודרניות, ואף RTOS-ים רבים ודרייברים של מערכות קבצים, כבר מבצעים חציצה בצד שלהם. הוספת שכבת חציצה נוספת אינה יעילה (בעיה הידועה בשם ”bufferbloat“) וצורכת זיכרון יקר. שימו לב שעדיין יש מקרים שבהם חציצה עשויה להיות שימושית, ולכן ייתכן שנציג תמיכה אופציונלית בחציצה בעתיד.

אך ב-CPython, דיכוטומיה חשובה נוספת קשורה ב“מצב החציצה“ - האם זרם עלול לבצע קריאות/כתיבות קצרות אם לאו. קריאה קצרה היא כאשר משתמש מבקש למשל 10 בתים מזרם, אך מקבל פחות, ובדומה לכך לגבי כתיבות. ב-CPython, זרמים ללא חיץ חשופים אוטומטית לפעולות קצרות, בעוד שזרמים עם חיץ מובטחים מפניהן. תכונת היעדר הקריאות/כתיבות הקצרות היא תכונה חשובה, שכן היא מאפשרת לפתח תוכניות תמציתיות ויעילות יותר - דבר שהוא רצוי מאוד עבור MicroPython. כך, אף ש-MicroPython אינו תומך בזרמים עם חיץ, הוא עדיין מספק זרמים ללא פעולות קצרות. האם יהיו פעולות קצרות או לא תלוי בצורכי כל מחלקה ספציפית, אך מומלץ בחום למפתחים להעדיף התנהגות ללא פעולות קצרות מהסיבות שצוינו לעיל. למשל, סוקטים של MicroPython מובטחים מפני קריאות/כתיבות קצרות. למעשה, נכון לעכשיו אין דוגמה למחלקת זרם עם פעולות קצרות בליבה, ומחלקה כזו תהיה ספציפית לחומרה מסוימת.

ההתנהגות ללא פעולות קצרות נעשית מורכבת במקרה של זרמים שאינם חוסמים, כאשר התנהגות חוסמת לעומת לא חוסמת היא דיכוטומיה נוספת של CPython, הנתמכת במלואה ב-MicroPython. זרמים שאינם חוסמים לעולם אינם ממתינים להגעת נתונים או לכתיבתם - הם קוראים/כותבים ככל הניתן, או מסמנים על מחסור בנתונים (או על יכולת לכתוב נתונים). ברור שהדבר מתנגש במדיניות ”ללא פעולות קצרות“, ואכן, מקרה של זרמים שאינם חוסמים עם חיץ (וכך ללא פעולות קצרות) הוא מורכב ב-CPython - בחלק מהמקומות שילוב כזה אסור, בחלקם הוא בלתי מוגדר או פשוט לא מתועד, ובמקרים מסוימים הוא מעלה חריגות מפורטות. העניין פשוט הרבה יותר ב-MicroPython: זרמים שאינם חוסמים חשובים לפעולות אסינכרוניות יעילות, ולכן תכונה זו גוברת על תכונת ”ללא פעולות קצרות“. כך, בעוד שזרמים חוסמים יימנעו מקריאות/כתיבות קצרות בכל עת שניתן (המקרה היחיד לקבל קריאה קצרה הוא אם הגענו לסוף הקובץ, או במקרה של שגיאה (אך שגיאות אינן מחזירות נתונים קצרים, אלא מעלות חריגות)), זרמים שאינם חוסמים עשויים להפיק נתונים קצרים כדי להימנע מחסימת הפעולה.

הדיכוטומיה האחרונה היא זרמים בינאריים לעומת זרמי טקסט. MicroPython תומך בהם כמובן, אך בעוד שב-CPython זרמי טקסט הם בעלי חיץ במהותם, ב-MicroPython הם אינם כך. (אכן, זהו אחד המקרים שעבורם ייתכן שנציג תמיכה בחציצה.)

שימו לב שלמען היעילות, MicroPython אינו מספק מחלקות בסיס מופשטות התואמות להיררכיה לעיל, ולא ניתן לממש או ליצור מחלקת משנה של מחלקת זרם ב-Python טהור.

פונקציות

io.open(name: str, mode: str = 'r', **kwargs) Any

פתיחת קובץ. הפונקציה הבילט-אין open() היא כינוי לפונקציה זו. הפרמטר mode נתמך תמיד; התמיכה בארגומנטים אחרים עשויה להשתנות.

מחלקות

class io.IOBase

מחלקת בסיס לאובייקטי זרם (”דמויי קובץ“). מחלקות משנה קונקרטיות מממשות את שיטות הקלט/פלט ברמה הנמוכה שלהלן (readinto, write, ioctl); סביבת הריצה בונה מעליהן את פרוטוקול הזרם ברמה הגבוהה יותר (read, readline, readlines, close, איטרציה), כך שכל מופע זרם תומך בשיטות אלה גם כאשר מחלקת המשנה אינה מגדירה אותן.

שיטות מימוש (דרסו אותן במחלקת משנה):

readinto(buf: bytearray) int | None

קריאת בתים אל החוצץ הניתן לכתיבה buf. החזרת מספר הבתים שנקראו, 0 בסוף הזרם, או None אם אין כעת נתונים זמינים (עבור זרם שאינו חוסם).

write(buf: bytes) int | None

כתיבת הבתים שב-buf. החזרת מספר הבתים שנכתבו, או None אם לא ניתן לבצע את הכתיבה כעת (עבור זרם שאינו חוסם).

ioctl(request: int, arg: int) int

בקרה על הזרם/ההתקן הבסיסי. request הוא אחד מקודי הבקשה MP_STREAM_*. החזרת ערך אי-שלילי בהצלחה, או ערך errno שלילי בשגיאה.

שיטות פרוטוקול הזרם (זמינות בכל מופע זרם):

read(size: int = -1)

קריאה והחזרה של עד size בתים (או תווים, במצב טקסט). אם size מושמט או שלילי, הקריאה נמשכת עד סוף הזרם. מחזירה bytes עבור זרמים בינאריים ו-str עבור זרמי טקסט; תוצאה ריקה מציינת את סוף הזרם.

readline(size: int = -1)

קריאה והחזרה של שורה אחת, כולל תו השורה החדשה הסופי אם קיים. אם size ניתן, נקראים לכל היותר size בתים (או תווים). מחזירה bytes / str ריקים בסוף הזרם.

readlines() list

קריאה עד סוף הזרם והחזרת list של שורות, כל אחת עם תו השורה החדשה הסופי שלה.

close() None

סגירת הזרם ושחרור כל המשאבים הבסיסיים. פעולות על זרם סגור מעלות OSError (או ValueError עבור זרמים בזיכרון).

seek(offset: int, whence: int = 0) int

שינוי מיקום הזרם הנוכחי ל-offset בתים יחסית ל-whence (0 = תחילת הזרם, 1 = המיקום הנוכחי, 2 = סוף הזרם). החזרת המיקום המוחלט החדש. מעלה OSError על זרם שאינו ניתן לחיפוש מיקום.

tell() int

החזרת המיקום המוחלט הנוכחי בזרם. שקול ל-seek(0, 1).

flush() None

ריקון כל חוצצי הכתיבה, ודחיפת נתונים ממתינים אל ההתקן או הקובץ הבסיסי. ללא השפעה על זרמים שאינם מבצעים חציצה.

איטרציה ישירה על זרם מפיקה שורה אחת בכל איטרציה – שקול לקריאה ל-readline() בלולאה עד שמוחזר ערך הסימון של סוף-זרם (שורה ריקה). זרם תומך גם בפרוטוקול מנהל ההקשר, כך ש-with open(...) as f: סוגר את הזרם אוטומטית.

הערה

מודול הזרמים של MicroPython חושף גם פונקציות עזר ב-C בעלות סיומת ”1“ mp_stream_read1_obj, mp_stream_readinto1_obj ו-mp_stream_write1_obj שמבצעות קריאת קלט/פלט בסיסית אחת במקום ללולל עד שהבקשה מסופקת במלואה. הן משמשות פנימית מחלקות כגון machine.UART למימוש ה-read / write שלהן – אך אף מחלקת זרם סטנדרטית אינה קושרת אותן כשיטות read1 / readinto1 / write1 הניתנות לקריאה מ-Python.

class io.StringIO(string: str = '')

אובייקט דמוי קובץ בזיכרון עבור קלט/פלט במצב טקסט (בדומה לקובץ רגיל שנפתח עם המאפיין ”t“). ניתן לציין תוכן התחלתי באמצעות הפרמטר string (שאמור להיות מחרוזת רגילה). מופעים תומכים גם בפרוטוקול מנהל ההקשר (ניתן לשימוש בהוראת with).

read(size: int = -1) str

קריאה והחזרה של עד size תווים. אם size מושמט או שלילי, נקרא ומוחזר כל התוכן הנותר.

readline(size: int = -1) str

קריאה והחזרה של שורה אחת. אם size ניתן, נקראים לכל היותר size תווים.

readinto(buf: bytearray) int

קריאה אל החוצץ הניתן לכתיבה שהוקצה מראש buf והחזרת מספר הבתים שנקראו.

write(s: str) int

כתיבת המחרוזת s והחזרת מספר התווים שנכתבו.

seek(offset: int, whence: int = 0) int

שינוי מיקום הזרם ל-offset יחסית ל-whence (0 = התחלה, 1 = נוכחי, 2 = סוף) והחזרת המיקום המוחלט החדש.

tell() int

החזרת מיקום הזרם הנוכחי.

flush() None

ריקון חוצצי הכתיבה. ללא השפעה עבור זרם בזיכרון.

close() None

סגירת הזרם ושחרור החוצץ הבסיסי. פעולות נוספות על זרם סגור מעלות ValueError.

getvalue() str

החזרת התוכן הנוכחי של החוצץ הבסיסי.

class io.StringIO(alloc_size: int)

יצירת אובייקט StringIO ריק שהוקצה מראש להכיל עד alloc_size בתים, כך שכתיבה של עד אותו מספר בתים לא תקצה מחדש את החוצץ (תוך הימנעות ממצב של חוסר זיכרון או מקיטוע זיכרון). בנאי זה הוא הרחבה של MicroPython המומלצת רק למקרים מיוחדים ולספריות ברמת המערכת, ולא ליישומי משתמש קצה.

הבדל מ-CPython

בנאי זה הוא הרחבה של MicroPython.

read(size: int = -1) str

קריאה והחזרה של עד size תווים. אם size מושמט או שלילי, נקרא ומוחזר כל התוכן הנותר.

readline(size: int = -1) str

קריאה והחזרה של שורה אחת. אם size ניתן, נקראים לכל היותר size תווים.

readinto(buf: bytearray) int

קריאה אל החוצץ הניתן לכתיבה שהוקצה מראש buf והחזרת מספר הבתים שנקראו.

write(s: str) int

כתיבת המחרוזת s והחזרת מספר התווים שנכתבו.

seek(offset: int, whence: int = 0) int

שינוי מיקום הזרם ל-offset יחסית ל-whence (0 = התחלה, 1 = נוכחי, 2 = סוף) והחזרת המיקום המוחלט החדש.

tell() int

החזרת מיקום הזרם הנוכחי.

flush() None

ריקון חוצצי הכתיבה. ללא השפעה עבור זרם בזיכרון.

close() None

סגירת הזרם ושחרור החוצץ הבסיסי. פעולות נוספות על זרם סגור מעלות ValueError.

getvalue() str

החזרת התוכן הנוכחי של החוצץ הבסיסי.

class io.BytesIO(string: bytes = b'')

אובייקט דמוי קובץ בזיכרון עבור קלט/פלט במצב בינארי (בדומה לקובץ רגיל שנפתח עם המאפיין ”b“). ניתן לציין תוכן התחלתי באמצעות הפרמטר string (שאמור להיות אובייקט bytes). מופעים תומכים גם בפרוטוקול מנהל ההקשר (ניתן לשימוש בהוראת with).

read(size: int = -1) bytes

קריאה והחזרה של עד size בתים. אם size מושמט או שלילי, נקרא ומוחזר כל התוכן הנותר.

readline(size: int = -1) bytes

קריאה והחזרה של שורה אחת. אם size ניתן, נקראים לכל היותר size בתים.

readinto(buf: bytearray) int

קריאה אל החוצץ הניתן לכתיבה שהוקצה מראש buf והחזרת מספר הבתים שנקראו.

write(b: bytes) int

כתיבת האובייקט דמוי הבתים b והחזרת מספר הבתים שנכתבו.

seek(offset: int, whence: int = 0) int

שינוי מיקום הזרם ל-offset יחסית ל-whence (0 = התחלה, 1 = נוכחי, 2 = סוף) והחזרת המיקום המוחלט החדש.

tell() int

החזרת מיקום הזרם הנוכחי.

flush() None

ריקון חוצצי הכתיבה. ללא השפעה עבור זרם בזיכרון.

close() None

סגירת הזרם ושחרור החוצץ הבסיסי. פעולות נוספות על זרם סגור מעלות ValueError.

getvalue() bytes

החזרת התוכן הנוכחי של החוצץ הבסיסי.

class io.BytesIO(alloc_size: int)

יצירת אובייקט BytesIO ריק שהוקצה מראש להכיל עד alloc_size בתים, כך שכתיבה של עד אותו מספר בתים לא תקצה מחדש את החוצץ (תוך הימנעות ממצב של חוסר זיכרון או מקיטוע זיכרון). בנאי זה הוא הרחבה של MicroPython המומלצת רק למקרים מיוחדים ולספריות ברמת המערכת, ולא ליישומי משתמש קצה.

הבדל מ-CPython

בנאי זה הוא הרחבה של MicroPython.

read(size: int = -1) bytes

קריאה והחזרה של עד size בתים. אם size מושמט או שלילי, נקרא ומוחזר כל התוכן הנותר.

readline(size: int = -1) bytes

קריאה והחזרה של שורה אחת. אם size ניתן, נקראים לכל היותר size בתים.

readinto(buf: bytearray) int

קריאה אל החוצץ הניתן לכתיבה שהוקצה מראש buf והחזרת מספר הבתים שנקראו.

write(b: bytes) int

כתיבת האובייקט דמוי הבתים b והחזרת מספר הבתים שנכתבו.

seek(offset: int, whence: int = 0) int

שינוי מיקום הזרם ל-offset יחסית ל-whence (0 = התחלה, 1 = נוכחי, 2 = סוף) והחזרת המיקום המוחלט החדש.

tell() int

החזרת מיקום הזרם הנוכחי.

flush() None

ריקון חוצצי הכתיבה. ללא השפעה עבור זרם בזיכרון.

close() None

סגירת הזרם ושחרור החוצץ הבסיסי. פעולות נוספות על זרם סגור מעלות ValueError.

getvalue() bytes

החזרת התוכן הנוכחי של החוצץ הבסיסי.