8.12. مديرو السياق غير المتزامنون

قدّمت نظرة عامة على Python مديري السياق -- وهي الكائنات التي تقودها كتل with، حيث يُشغَّل __enter__ عند الدخول و __exit__ عند الخروج بغض النظر عن كيفية انتهاء الكتلة. ومديرو السياق غير المتزامنون هم الفكرة ذاتها منقولة إلى asyncio: فـ __aenter__ و __aexit__ هما دالتان تعاونيتان، بحيث يمكن للإعداد أو التنظيف انتظار شيء ما بـ await. والكتلة التي تقودهما هي async with.

لقد استخدمنا واحداً منهم بالفعل. فـ asyncio.Lock هو مدير سياق غير متزامن؛ وقد استخدمته صفحة الأقفال على هيئة async with bus_lock: .... هذه الصفحة تتناول كتابة واحد لمورد خاص بالتطبيق.

8.12.1. متى تكتب واحداً

عندما يكون لدى التطبيق مورد يحتاج إلى إعداد وتفكيك مقترنين، ويتعين على أحد هذين الجانبين على الأقل انتظار شيء ما بـ await. اتصالات الشبكة، والمستشعرات التي تحتاج إلى تأخير استقرار بعد التهيئة، وأي شيء يقفل شيئاً عند الدخول ويفتحه عند الخروج. لا تنطبق صيغة with المتزامنة البسيطة لأن __enter__ و __exit__ الخاصين بها لا يمكن أن يكونا دالتين تعاونيتين.

8.12.2. الطريقتان

  • تُشغَّل async def __aenter__(self) عند دخول الكتلة. وما تُرجعه هو ما ترتبط به عبارة as name الاختيارية لـ async with. وإرجاع self هو الشكل الأكثر شيوعاً، لكن أي قيمة تصلح.

  • تُشغَّل async def __aexit__(self, exc_type, exc, tb) عند الخروج من الكتلة. ويكون exc_type هو None عند الخروج الطبيعي؛ وعند وقوع استثناء (أو إلغاء) يكون فئة الاستثناء، ويكون exc هو النسخة. وإرجاع قيمة صادقة يخبر Python بأن الاستثناء قد عُولِج ولا ينبغي أن ينتشر. أما إرجاع None (الحالة المعتادة) فيدع الاستثناء يتابع صعوداً عبر سلسلة الاستدعاء بعد تشغيل التنظيف.

8.12.3. مثال تطبيقي

غلاف كشّاف ضوئي يُشغّل LED طوال جسم الكتلة، مع تأخير استقرار قصير كي تكون الإضاءة ثابتة قبل حدوث أي عمليات التقاط، ثم يطفئ LED مجدداً عند الخروج:

import asyncio
from machine import LED


class Spotlight:
    def __init__(self, led):
        self._led = led

    async def __aenter__(self):
        self._led.on()
        await asyncio.sleep_ms(50)
        return self

    async def __aexit__(self, exc_type, exc, tb):
        self._led.off()

async def main():
    led = LED("LED_WHITE")

    async with Spotlight(led):
        # work that benefits from steady illumination
        await asyncio.sleep_ms(200)

asyncio.run(main())

عند دخول الكتلة، يُشغَّل __aenter__: يُشغَّل LED، ويتنازل انتظار الاستقرار البالغ 50 ms عبر await عن التحكم للحلقة كي تتمكن الدوال التعاونية الأخرى من إحراز تقدم في غضون ذلك، ويبدأ جسم الكتلة حالما يكتمل الانتظار. وعند انتهاء الكتلة -- عند خروج طبيعي، أو عند استثناء، أو عند إلغاء -- يُشغَّل __aexit__ ويُطفأ LED مجدداً. يُشغَّل التنظيف في كل حالة؛ وهذا هو الضمان الذي توفره async with.

The frame capture page shows how to make csi.CSI.snapshot() await-friendly; once that wrapper is in hand, the body of an async with Spotlight(led): block would typically be a capture loop running under the steady illumination.