io --- تدفقات الإدخال/الإخراج

تحتوي هذه الوحدة على أنواع إضافية من كائنات stream (شبيهة بالملفات) ودوال مساعدة. وهي تعرض الدالة المدمجة open() إلى جانب مخازن نصية وثنائية في الذاكرة (StringIO، BytesIO) تنفّذ واجهة التدفق القياسية read/write/seek.

التسلسل المفاهيمي

الفرق عن CPython

تم تبسيط التسلسل المفاهيمي لفئات التدفق الأساسية في MicroPython، كما هو موضح في هذا القسم.

تلتزم فئات التدفق الأساسية (المجرّدة)، التي تشكّل أساسًا لسلوك جميع الفئات المحدّدة، ببضعة تقسيمات ثنائية (تصنيفات زوجية) في CPython. أما في MicroPython فقد جرى تبسيطها إلى حد ما وجعلها ضمنية لتحقيق كفاءة أعلى وتوفير الموارد.

من التقسيمات الثنائية المهمة في CPython التمييز بين التدفقات غير المخزّنة مؤقتًا والمخزّنة مؤقتًا. في MicroPython، جميع التدفقات حاليًا غير مخزّنة مؤقتًا. ويعود ذلك إلى أن جميع أنظمة التشغيل الحديثة، بل وكثيرًا من أنظمة التشغيل في الزمن الحقيقي (RTOS) ومشغّلات أنظمة الملفات، تقوم بالتخزين المؤقت من جانبها. وإضافة طبقة أخرى من التخزين المؤقت أمر يأتي بنتائج عكسية (مشكلة تُعرف باسم "bufferbloat") ويستهلك ذاكرة ثمينة. لاحظ أنه لا تزال هناك حالات قد يكون فيها التخزين المؤقت مفيدًا، لذا قد نطرح دعمًا اختياريًا للتخزين المؤقت في وقت لاحق.

لكن في CPython، يرتبط تقسيم ثنائي مهم آخر بـ"التخزين المؤقت" - وهو ما إذا كان التدفق قد يتعرّض لعمليات قراءة/كتابة قصيرة أم لا. القراءة القصيرة هي عندما يطلب المستخدم مثلًا 10 بايتات من تدفق، لكنه يحصل على أقل من ذلك، والأمر مماثل بالنسبة للكتابة. في CPython، تكون التدفقات غير المخزّنة مؤقتًا عرضة تلقائيًا للعمليات القصيرة، بينما التدفقات المخزّنة مؤقتًا مضمونة ضدها. وانعدام القراءة/الكتابة القصيرة سمة مهمة، إذ تتيح تطوير برامج أكثر إيجازًا وكفاءة - وهو أمر مرغوب فيه بشدة في MicroPython. لذلك، ومع أن MicroPython لا تدعم التدفقات المخزّنة مؤقتًا، فإنها لا تزال توفّر تدفقات خالية من العمليات القصيرة. وما إذا كانت ستحدث عمليات قصيرة أم لا يعتمد على احتياجات كل فئة بعينها، لكن يُنصح المطورون بشدة بتفضيل سلوك انعدام العمليات القصيرة للأسباب المذكورة أعلاه. على سبيل المثال، تضمن مقابس (sockets) 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

إرجاع المحتويات الحالية للمخزن المؤقت الأساسي.