select --- الانتظار للأحداث على مجموعة من التدفقات

توفّر هذه الوحدة دوالاً للانتظار بكفاءة حتى يصبح تدفق واحد أو أكثر من التدفقات (كائنات شبيهة بالملفات تُنفّذ بروتوكول التدفق، مثل المقابس وUARTs وكائنات الإدخال/الإخراج الأخرى) جاهزاً للقراءة أو الكتابة، بدلاً من الانتظار المشغول أو الحجب على كائن واحد.

كائن poll هو الواجهة المُوصى بها: فهو يتوسّع لعدد كبير من التدفقات وخالٍ من التخصيص عند استخدامه عبر poll.ipoll(). أما الدالة select() على مستوى الوحدة فهي واجهة توافق أقل كفاءة.

مثال باستخدام كائن poll

import select

poller = select.poll()
poller.register(uart, select.POLLIN)

while True:
    # Wait up to 1000 ms for the UART to have data.
    for obj, event in poller.poll(1000):
        if event & select.POLLIN:
            print(obj.read())

مثال باستخدام الدالة select() على مستوى الوحدة:

import select

# Wait up to 1 second for the socket to become readable.
rlist, wlist, xlist = select.select([sock], [], [], 1.0)
if rlist:
    data = sock.recv(100)

الدوال

select.select(rlist: List, wlist: List, xlist: List, timeout: float | None = None) Tuple[List, List, List]

ينتظر حتى يصبح كائن تدفق واحد أو أكثر من الكائنات المُعطاة جاهزاً، أو حتى تنقضي مهلة timeout.

  • rlist هي قائمة بالكائنات المراد مراقبتها لقابلية القراءة.

  • wlist هي قائمة بالكائنات المراد مراقبتها لقابلية الكتابة.

  • xlist هي قائمة بالكائنات المراد مراقبتها لحالة خطأ أو انقطاع.

  • timeout هو الحد الأقصى للوقت المراد الانتظار له، بالثواني (يُقبل عدد عشري). إذا حُذِف أو كان None فإن الاستدعاء يحجب إلى أجل غير مسمى؛ أما 0 فيُرجِع فوراً (استطلاع غير حاجب).

يُرجِع صفاً ثلاثياً من القوائم (rlist, wlist, xlist). كل قائمة مُرجَعة هي المجموعة الفرعية من قائمة الدخل المقابلة التي تحتوي على الكائنات التي أصبحت جاهزة للقراءة، أو جاهزة للكتابة، أو أشارت إلى خطأ/انقطاع، على التوالي. إذا انقضت مهلة timeout دون أن يكون أي شيء جاهزاً، تكون القوائم الثلاث جميعها فارغة.

هذه الدالة أقل كفاءة من poll (فهي تعيد بناء مجموعة الاستطلاع الداخلية في كل استدعاء)؛ استخدم poll بدلاً منها حيثما أمكن.

الثوابت

select.POLLIN: int

تتوفّر بيانات للقراءة من التدفق.

select.POLLOUT: int

يمكن للتدفق قبول مزيد من البيانات للكتابة.

select.POLLERR: int

حدثت حالة خطأ على التدفق. هذا حدث غير مطلوب: تُبلِّغ عنه poll.poll() / poll.ipoll() حتى لو لم يُطلَب في eventmask، ولا يصحّ تمريره كـ eventmask للدخل.

select.POLLHUP: int

انقطع التدفق / تم فصله. هذا حدث غير مطلوب: تُبلِّغ عنه poll.poll() / poll.ipoll() حتى لو لم يُطلَب في eventmask، ولا يصحّ تمريره كـ eventmask للدخل.

الأصناف

class select.poll

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

تُضاف التدفقات بـ register()، وتُزال بـ unregister()، ويمكن تغيير مجموعة الأحداث المراد مراقبتها بـ modify(). بمجرد التهيئة، استدعِ poll() للحجب حتى يصبح شيء ما جاهزاً (أو تنقضي مهلة)، أو ipoll() للحصول على متغير قائم على المُكرِّر وخالٍ من التخصيص.

register(obj: Any, eventmask: int = select.POLLIN | select.POLLOUT) None

يسجّل التدفق obj للاستطلاع، مراقباً الأحداث المُعطاة بواسطة eventmask (وهي عملية OR المنطقية لما يلي):

  • select.POLLIN -- تتوفّر بيانات للقراءة

  • select.POLLOUT -- يمكن للتدفق قبول مزيد من البيانات للكتابة

eventmask يكون افتراضياً select.POLLIN | select.POLLOUT.

select.POLLHUP و select.POLLERR غير صالحَين في eventmask للدخل -- فهما حدثان غير مطلوبَين تُبلِّغ عنهما poll() / ipoll() بغض النظر عما إذا طُلِبا (وهذا يطابق دلالات POSIX).

لا بأس باستدعاء هذه الطريقة أكثر من مرة لنفس obj: فالاستدعاء اللاحق يحدّث قناع أحداث obj، ويتصرّف مثل modify().

unregister(obj: Any) None

يزيل obj من مجموعة التدفقات المسجّلة. وليس من الخطأ إلغاء تسجيل obj غير مسجّل حالياً (ليس للاستدعاء أي تأثير في تلك الحالة).

modify(obj: Any, eventmask: int) None

يغيّر قناع الأحداث لـ obj مسجّل بالفعل إلى eventmask. يُطلِق OSError مع errno.ENOENT إذا لم يكن obj مسجّلاً.

poll(timeout: int = -1, /) List[Tuple]

يحجب حتى يصبح تدفق مسجّل واحد على الأقل جاهزاً أو يشير إلى حالة استثنائية، ثم يُرجِع قائمة التدفقات التي أُطلِقت.

timeout هو الحد الأقصى للوقت المراد الانتظار له بالـ ملي ثانية. إذا حُذِف أو كان -1 فإن الاستدعاء يحجب إلى أجل غير مسمى؛ أما 0 فيُرجِع فوراً (استطلاع غير حاجب).

يُرجِع قائمة من الصفوف (obj, event, ...)، صف واحد لكل تدفق أُطلِق. obj هو التدفق المسجّل و event هو عملية OR على مستوى البتات لرايات select.POLLIN / select.POLLOUT / select.POLLERR / select.POLLHUP التي حدثت. قد يحتوي كل صف على عناصر إضافية مُحدَّدة من قبل التنفيذ، لذا لا تفترض أن طوله 2 بالضبط. إذا انقضت مهلة timeout دون أن يكون أي شيء جاهزاً، تُرجَع قائمة فارغة.

قد تُرجَع select.POLLHUP و select.POLLERR في أي وقت (حتى لو لم تُطلَب) ويجب التعامل معها -- عادةً بإلغاء تسجيل التدفق المتأثر وإغلاقه -- وإلا فإن الاستدعاءات اللاحقة ستظل تُرجِع فوراً مع ضبط هاتين الرايتين لذلك التدفق.

يُضمَن تشغيل أي دوال رد نداء مجدولة معلّقة قبل الدخول إلى حلقة الاستطلاع.

الاختلاف عن CPython

قد تحتوي الصفوف المُرجَعة على أكثر من عنصرين، كما هو موضّح أعلاه.

ipoll(timeout: int = -1, flags: int = 0, /) Iterator[Tuple]

مثل poll()، ولكن بدلاً من بناء قائمة فإنها تُرجِع مُكرِّراً يُنتِج صفاً واحداً (obj, event, ...) في كل مرة. هذا يتجنّب التخصيص في كل استدعاء، وهو أمر مهم لمجدولات الإدخال/الإخراج غير المتزامنة.

الصف المُنتَج مملوك من قبل المُستدعَى: فهو يُعاد استخدامه (يُكتب فوقه) في التكرار التالي، لذا يجب استهلاك محتوياته داخل جسم الحلقة ويجب عدم تخزين الإشارات إليه.

timeout له نفس المعنى كما في poll(). إذا كان flags يساوي 1، يُستخدم سلوك الإطلاق لمرة واحدة: فالتدفق الذي أُطلِق حدثه يُمسح قناع أحداثه تلقائياً (وهو ما يكافئ poll.modify(obj, 0))، فلا يُبلَّغ عن أي أحداث أخرى له حتى يُضبط قناعه مرة أخرى بـ modify().

يُضمَن تشغيل أي دوال رد نداء مجدولة معلّقة قبل الدخول إلى حلقة الاستطلاع.

الاختلاف عن CPython

هذه الطريقة هي امتداد خاص بـ MicroPython.