سؤال

أقوم بتضمين مترجم Python في برنامج C.ومع ذلك، قد يحدث ذلك أثناء تشغيل بعض البرامج النصية لبايثون عبر PyRun_SimpleString() سيتم تشغيله في حلقة لا نهائية أو سيتم تنفيذه لفترة طويلة جدًا.يعتبر PyRun_SimpleString("while 1: pass"); في منع البرنامج الرئيسي من الحظر، اعتقدت أنه يمكنني تشغيل المترجم في سلسلة رسائل.

كيف أتوقف عن تنفيذ البرنامج النصي python في المترجم المضمن الذي يعمل في سلسلة رسائل دون إنهاء العملية برمتها؟

هل من الممكن تمرير استثناء للمترجم؟هل يجب أن أقوم بتغليف البرنامج النصي تحت برنامج نصي آخر يستمع إلى الإشارات؟

ملاحظة:يمكنني تشغيل الثعبان في عملية منفصلة ولكن هذا ليس ما أريده - إلا إذا كان هذا هو الملاذ الأخير ...


تحديث:

لذلك، فإنه يعمل الآن.شكرًا لك دينيس أوتكيداش، مرة أخرى!

إذا رأيت هذا صحيحًا، عليك القيام بأمرين:اطلب من المترجم أن يتوقف و return -1 في نفس الموضوع الذي يتم فيه تشغيل PyRun_SimpleString().

للتوقف، هناك عدة احتمالات: PyErr_SetString(PyExc_KeyboardInterrupt, "...") أو PyErr_SetInterrupt() - قد يترك الأمر الأول بايثون يقوم بتشغيل بعض التعليمات الإضافية ثم يتوقف، أما الأخير فيوقف التنفيذ على الفور.

ل return -1 انت تستخدم Py_AddPendingCall() لإدخال استدعاء دالة في تنفيذ بايثون.تذكره المستندات منذ الإصدار 2.7 و3.1 ولكنه يعمل على Pythons الأقدم أيضًا (2.6 هنا).بدءًا من الإصدار 2.7 و3.1، يجب أيضًا أن يكون آمنًا لسلسلة الرسائل، مما يعني أنه يمكنك الاتصال به دون الحصول على GIL (؟).

لذا يمكن للمرء إعادة كتابة المثال أدناه:

int quit() {
    PyErr_SetInterrupt();
    return -1;
}
هل كانت مفيدة؟

المحلول

يمكنك استخدام Py_AddPendingCall() لإضافة استثناء رفع دالة ليتم استدعاؤه في الفاصل الزمني التالي للتحقق (راجع المستندات في sys.setcheckinterval() لمزيد من المعلومات).هنا مثال مع Py_Exit() call (الذي يناسبني، ولكن ربما ليس هو ما تحتاجه)، استبدله بـ Py_Finalize() أو واحد من PyErr_Set*():

int quit(void *) {
    Py_Exit(0);
}


PyGILState_STATE state = PyGILState_Ensure();
Py_AddPendingCall(&quit, NULL);
PyGILState_Release(state);

يجب أن يكون هذا كافيًا لأي كود بيثون خالص.لكن لاحظ أن بعض وظائف C يمكن تشغيلها لفترة من الوقت كعملية واحدة (كان هناك مثال على بحث regexp طويل الأمد، لكنني لست متأكدًا من أنه لا يزال ذا صلة).

نصائح أخرى

حسنًا، يجب أن يعمل مترجم بايثون في سلسلة رسائل منفصلة عن برنامج التضمين وإلا فلن تحصل على فرصة لمقاطعة المترجم.

عندما يكون لديك ذلك، ربما يمكنك استخدام أحد استدعاءات استثناء Python API لتشغيل استثناء في المترجم؟يرى استثناءات واجهة برمجة تطبيقات Python C

أردت أن أكون قادرًا على مقاطعة أي كتلة تعليمات برمجية قيد التشغيل في بايثون، بما في ذلك إلغاء حلقة لا نهائية أو مجرد بعض التعليمات البرمجية طويلة الأمد.في طلبي، يتم تقديم وتقييم كود Python ذو الخيط الواحد.يمكن أن يأتي طلب المقاطعة في أي وقت.كان هذا السؤال والجواب حاسمين في مساعدتي على تحقيق ذلك بالفعل.

بالنظر إلى هذا السؤال في عام 2019، فإن الحل هنا لم ينجح معي؛حاولت استخدامها Py_AddPendingCall(&quit, NULL); بعدة طرق مختلفة ولكن الوظيفة المطلوبة لم يتم تنفيذها أبدًا كما هو متوقع، أو لم يتم تنفيذها على الإطلاق.تسبب هذا في توقف محرك بايثون ثم تعطله في النهاية عندما قمت بالإرسال Py_FinalizeEx().وأخيراً وجدت ما أحتاجه في هذا مفيد جداً إجابة إلى سؤال مماثل.

بعد أن أبدأ تشغيل محرك بايثون، أقوم باسترداد معرف مؤشر الترابط باستخدام ملف threading الحزمة في كود بايثون.أقوم بتحليل ذلك إلى قيمة طويلة وحفظها.

وظيفة المقاطعة هي في الواقع بسيطة للغاية:
PyGILState_Ensure()
PyThreadState_SetAsyncExc(threadId, PyExc_Exception) باستخدام معرف مؤشر الترابط الذي قمت بحفظه من قبل ونوع الاستثناء العام
PyGILState_Release()

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top