﷽
نحتاج في بعض الأحيان للتعامل مع تطبيقات تتطلب مدخلا من المستخدم وفي عالم البرمجة فإننا كثيرا ما نحول الجهد البشري إلى آلي فبدل انتظار المستخدم لإدخال كلمة سر أو الاجابة بشكل ما فإننا نجعل ذلك آليا عن طريق النص البرمجي وهذا مفيد جدا في الأتمتة والاختبار.
برمجية Expect
في الصدفة أو الطرفية shell هناك أمر expect و autoexpect حيث يستخدمان لأتمتة برمجيات الطرفية فبعد تشغيل autoexpect ووضع نص برمجي ب bash أو sh كمعطى فإنه يسجل كيف يتعامل المستخدم مع هذا النص البرمجي ويسجل إجابات الحوار كاملا في سكربت بصيغة expect
بعدها يستخدم أمر expect لتشغيل النص البرمجي الذي ولدته autoexpect، لكن هذا في الطرفية فماذا عن فعل ذلك باستخدام بايثون بحيث يمكننا التحكم بسهولة أكبر بالعملية؟
برمجية Pexpect
هي برمجية بايثون تمكننا من تقليد عمل expect لكن من داخل بايثون. ولم أعثر فيها على معادل لـ autoexpect لأتمته عملية الكود لكنه يمكن بجهد قليل كتابة تعليماتها.
مثال عملي:
لتثبيت البرمجية وعمل بيئة وهمية للعمل بها داخل دليل مخصص:
mkdir test_pexpect && cd test_pexpect
pipenv shell
pipenv install pexpect
محاكاة برمجية تتطلب مدخلات
الآن لنأخذ مثلا نص بايثون الآتي الذي يتطلب حوارا مع المستخدم:
print('Enter your name:')
x = input()
print(f'Hello {x}')
حيث نضع مدخلات المحاور في متغير x ثم نطبعها له بعد Hello.
لنحفظ هذا النص في ملف test_enter.py.
كتابة نص المحاورة باستخدام بايثون
لمحاورة هذه البرمجية آليا نكتب النص التالي مستخدمين expect:
import pexpect
import sys
p = pexpect.spawn("python3 test_enter.py") # تشغيل النص البرمجي باستخدام بايثون#
p.logfile_read = sys.stdout.buffer # جعل المخرجات والمدخلات مرئية وإلا سينفذ التعليمات بصمت #
p.expect("Enter your name:") # توقع النص بين قوسين من النص البرمجي المشغّل #
p.sendline("Zaid") # إرسال نص إلى البرمجية #
p.expect(pexpect.EOF)
لنحفظ الملف بصغية بايثون ونشغله فنرى الآتي:
$ python3 pexpect_test.py
Enter your name:
Zaid
Hello Zaid
كما نرى أرسل الأسم دون تدخل مننا بحسب قيمة مخزنة مسبقا في النص البرمجي.
ما الهدف من هذه العملية؟
أثناء تعاملي مع محفظة بيتكوين كاش التي أستخدمها في تشغيل عديد من البرمجيات فإني أحتاج لتجاوز طلب كلمة السر أثناء المحفظة، ورغم أن المحفظة لها واجهة سطر أوامر قوية وتدعم الأتمته لكن هذا الأمر فيه علة تتطلب إدخال يدوي من المستخدم.
وكنت قد ولدت لها السكربت التالي باستخدام autoexpect:
set timeout -1
spawn ./electron-cash --dir "" create
match_max 100000
expect -exact "Password (hit return if you do not wish to encrypt your wallet):"
send -- "\r"
expect eof
وبعد جهد توصلت للسكربت الآتي باستخدام pexpect:
import pexpect
import sys
python_exec = "/home/user/electron-cash-wallet/venv/bin/python"
ec_exec = "/home/user/electron-cash-wallet/src/Electron-Cash/electron-cash"
def create_wallet(python_exec, ec_exec, network):
print("Python: ", python_exec)
print("Electron Cash: ", ec_exec)
# Execute the command
p = pexpect.spawn(python_exec, [command, f"--{network}", "create"])
# Verbose, show messages
p.logfile_read = sys.stdout.buffer
p.delaybeforesend = 2
print("Exceution done")
# Expect the response
p.expect("Password \(hit return if you do not wish to encrypt your wallet\):")
print("Expected input received")
p.delaybeforesend = 1
# Send return key
p.sendline(b'\r')
print("Sent response")
p.expect(pexpect.EOF)
print("Script done")
شُكر
أود أن أشكر مجتمع بيتكوين كاش على تقديمه التمويل اللازم لكتابة هذه المقالات التعليمية ونشر المعرفة البرمجية والتوعية في مجالات العملات الرقمية في العالم العربي وتطويره لبرمجيات حرة تمكن آليات التمويل الجماعي غير المركزي ك Flipstarter و IPFS Flipstarter الذي يمكن أي شخص من عمل حملة تمويل جماعي دون الحاجة لوسيط أو حتى استضافة ودون الحاجة لمحفظة خاصة.

